2012-02-11 12 views

Respuesta

43

No hay una implementación específica para IO; es un tipo abstracto, con la implementación exacta no definida por el Informe Haskell. De hecho, no hay nada que detenga una implementación que implementa IO y su instancia Monad como primitivas del compilador, sin ninguna implementación de Haskell.

Básicamente, Monad se utiliza como una interfaz -IO, que solamente puede ser implementada en Haskell pura. Probablemente esto sea todo lo que necesita saber en esta etapa, y profundizar en los detalles de la implementación probablemente solo confunda, en lugar de dar una idea.

Dicho esto, si nos fijamos en el código fuente de GHC, encontrará que representa IO a como una función que parece State# RealWorld -> (# State# RealWorld, a #) (utilizando un unboxed tuple como el tipo de retorno), pero esto es engañoso; es un detalle de implementación, y estos valores State# RealWorld no existen realmente en tiempo de ejecución. IO es no una mónada de estado, en teoría o en la práctica.

En su lugar, GHC usa primitivas impuras para implementar estas operaciones de E/S; los "valores" State# RealWorld son solo para detener las instrucciones de reordenamiento del compilador introduciendo dependencias de datos de una declaración a la siguiente.

Pero si realmente quieres ver la implementación de return y (>>=) de GHC, aquí están:

returnIO :: a -> IO a 
returnIO x = IO $ \ s -> (# s, x #) 

bindIO :: IO a -> (a -> IO b) -> IO b 
bindIO (IO m) k = IO $ \ s -> case m s of (# new_s, a #) -> unIO (k a) new_s 

donde unIO simplemente desenvuelve la función desde el interior del IO constructor.

Es importante señalar que IO a representa una descripción de un cálculo impura que se podrían ejecutar para producir un valor de tipo a. El hecho de que haya una manera de obtener valores de la representación interna de GHC de IO no significa que esto se cumpla en general, o que pueda hacer tal cosa con todas las mónadas. Es puramente un detalle de implementación por parte de GHC.

El state monad es una mónada utilizada para acceder y mutar un estado a través de una serie de cálculos; se representa como s -> (a, s) (donde s es el tipo de estado), que se ve muy similar al tipo que utiliza GHC para IO, por lo tanto, la confusión.

+0

+1 para la gran explicación de por qué State # se usa para preservar el orden de las declaraciones! – aelguindy

+2

No estoy de acuerdo con que IO no sea una mónada de estado. Es solo que el estado que transportaba (comúnmente llamado 'RealWorld') es completamente abstracto. Este hecho no lo descalifica más como una verdadera Mónada de Estado que la naturaleza abstracta del 's'-thread en la' ST' Monad. –

+7

@ JohnF.Miller: El modelo de mónada de estado de 'IO' puede modelar el cálculo secuencial (aunque' IO' no se implementa de esa manera en la práctica), pero no puede representar razonablemente la concurrencia. De todos modos, es definitivamente más confuso que útil, dado que ese modelo de 'IO' es * mucho * más restringido que una mónada de estado (o podrías hacer cosas como poner el estado del mundo de nuevo después de realizar algún lado -efectos, revirtiéndolos). – ehird

13

Estará decepcionado, pero el >>= en IO mónada no es tan interesante. Para citar la fuente GHC:

{- | 
A value of type @'IO' [email protected] is a computation which, when performed, 
does some I\/O before returning a value of type @[email protected] 

There is really only one way to \"perform\" an I\/O action: bind it to 
@[email protected] in your program. When your program is run, the I\/O will 
be performed. It isn't possible to perform I\/O from an arbitrary 
function, unless that function is itself in the 'IO' monad and called 
at some point, directly or indirectly, from @[email protected] 

'IO' is a monad, so 'IO' actions can be combined using either the do-notation 
or the '>>' and '>>=' operations from the 'Monad' class. 
-} 
newtype IO a = IO (State# RealWorld -> (# State# RealWorld, a #)) 

Eso significa IO mónada es declarada como la instancia de State State# mónada, y su >>= se define allí (y su aplicación es bastante fácil de adivinar) .

Ver IO inside artículo en Haskell wiki para obtener más detalles sobre IO mónada. También es útil mirar Haskell docs, donde cada posición tiene un pequeño enlace de "Fuente" a la derecha.

Actualización: Y ahí va otra decepción, que es mi respuesta, porque no noté el '#' en State#. Sin embargo IO se comporta como State mónada llevar abstracta RealWorld estado

Como @ehird escribió State# es del compilador interno y >>= para la IO mónada se define en GHC.Base módulo:

instance Monad IO where 
    {-# INLINE return #-} 
    {-# INLINE (>>) #-} 
    {-# INLINE (>>=) #-} 
    m >> k = m >>= \ _ -> k 
    return = returnIO 
    (>>=)  = bindIO 
    fail s = failIO s 

returnIO :: a -> IO a 
returnIO x = IO $ \ s -> (# s, x #) 

bindIO :: IO a -> (a -> IO b) -> IO b 
bindIO (IO m) k = IO $ \ s -> case m s of (# new_s, a #) -> unIO (k a) new_s 
+1

+1 por mencionar el enlace de origen en Haskell docs – Sal

+1

Esto no es cierto, sin embargo. Eso es 'State #' not 'State', y como se discutió en otra parte, la magia real que está sucediendo en la món IO no es capturada por sus operaciones en la ficha de estado. –

+0

'GHC.IOBase' ha sido eliminado en [este compromiso] (http://git.haskell.org/packages/base.git/commit/00c0ee70fa4a8d14e583554584dc93704a69ba13). Las definiciones mencionadas ahora parecen estar en ['GHC.Base'] (http://hackage.haskell.org/package/base-4.7.0.2/docs/src/GHC-Base.html#bindIO). – jameshfisher

2

Ellos no hacen nada especial, y están ahí para secuenciar acciones. Sería de gran ayuda si usted piensa de ellos con diferentes nombres:

>> = se convierte en "y luego, usando el resultado de la acción anterior,"

>> convierte en "y luego,"

retorno se convierte en "no hacer nada, sino el resultado de no hacer nada es"

esto convierte a esta función:

main :: IO() 
main = putStr "hello" 
    >> return " world" 
    >>= putStrLn 

se convierte en:

main :: IO() 
main = putStr "hello" and then, 
     do nothing, but the result of doing nothing is " world" 
     and then, using the result of the previous action, putStrLn 

Al final, no hay nada mágico en IO. Es exactamente tan mágico como un punto y coma en C.

+2

Lo siento, pero he votado negativamente. Cuando algo * parece * mágico, simplemente decir que no es mágico no es suficiente: hay que apartar la cortina y mostrar cómo funcionó el truco de magia. –

+0

No hay nada mágico sobre IO. Es exactamente tan mágico como un punto y coma en C. –

+3

Pero eso no es del todo cierto. Los puntos y coma son un caso especial de la sintaxis básica de C. Los operadores '(>> =)' y '(>>)' y la función 'return' no tienen ese estado especial: ¡están definidos en una biblioteca! El hecho de que pueda, al menos a primera vista, definir un fragmento impuro del lenguaje como una biblioteca en un lenguaje que de otro modo sería masoquísticamente puro seguramente parece un poco misterioso si no sabe lo que está sucediendo. –

Cuestiones relacionadas