Pregunta

Estoy aprendiendo sobre las mónadas y tengo algunas preguntas.

Aquí es donde estoy ahora.Por favor corríjame donde me equivoco.

  • El >>= El símbolo es un operador infijo.Los operadores infijos son funciones que toman dos argumentos (lado izquierdo y lado derecho) y devuelven un valor.

  • El >>= El símbolo se llama operador de enlace y tiene firma. Monad m => m t -> (t -> m u) -> m u.Sin embargo, los tipos no parecen coincidir aquí.Obtenemos un valor de tipo m t y el segundo argumento es una función que toma un t.(No veo cómo conectar los puntos).

  • Esto debe significar que la función de vinculación es de alguna manera capaz de eliminar el m desde el m t para poder conseguir el t y pasarlo a la función.

Aquí están mis preguntas:

  • ¿Es la capacidad de eliminar el m de m t algo que sólo es posible dentro de dicho operador de enlace.¿Este operador de enlace tiene algunos privilegios especiales o algo así?

  • ¿Qué tiene que ver con los cambios de estado?Entiendo (creo) que el objetivo de las mónadas es "envolver" los efectos secundarios para que queden aislados del resto del programa.Pero ¿cuál es el papel del operador de enlace en esto?

¿Fue útil?

Solución

  

Es la capacidad de eliminar la 'M' de 'M t', algo que solo es posible dentro de dicho operador de enlace. ¿Este operador de enlace tiene privilegios especiales o algo así?

Bind no es de ninguna manera un caso especial, pero generalmente se definirá en el mismo módulo que el tipo de datos mónadas. Por lo tanto, puede conocer los detalles (y el uso) que el módulo no exporta. El caso habitual sería que el módulo exporta un tipo de datos, pero no sus constructores u otros detalles sobre la estructura interna de los tipos. Luego, para el código que usa el módulo, el funcionamiento interno del tipo de datos es invisible y ese código no puede modificar directamente los valores de este tipo.

Opuesto a que las funciones definidas dentro del módulo, como por ejemplo algún operador de enlace >>=, pueden acceder a lo que quieran desde el módulo en el que están definidas. Por lo tanto, tales funciones podrían hacer cosas & externo; " las funciones no pueden hacer.

Un caso especial es la mónada IO, ya que no está definida por un módulo, sino que está integrada en el sistema / compilador de tiempo de ejecución. Aquí el compilador conoce los detalles internos de su implementación y expone funciones como <=> 's <=>. De hecho, las implementaciones de estas funciones son especialmente privilegiadas ya que viven & "; Fuera del programa &"; Pero este es un caso especial y este hecho no debería ser observable desde Haskell.

  

¿Qué tiene que ver con los cambios de estado? Entiendo (creo) que el objetivo de las mónadas es 'envolver' los efectos secundarios para que estén aislados del resto del programa. Pero, ¿cuál es el papel del operador de enlace en esto?

Realmente no tiene que ver con los cambios de estado, este es solo un problema que se puede manejar con moands. La <=> mónada se usa para ejecutar IO en un cierto orden, pero generalmente las mónadas son solo formas de combinar funciones juntas.

Generalmente, una mónada (específicamente su función de vinculación) define una forma en la que ciertas funciones deben componerse para funciones más grandes. Este método de combinar funciones se abstrae en la mónada. Cómo funciona exactamente esta combinación o por qué desearía combinar funciones de tal manera no es importante, una mónada solo especifica una forma de combinar ciertas funciones de cierta manera. (Consulte también this & Quot; Mónadas para C # programadores " answer donde básicamente repito eso algunas veces con ejemplos.)

Otros consejos

  

es la capacidad de eliminar la 'M' de 'M t', algo que solo es posible dentro de dicho operador de enlace.

Bueno, ciertamente es posible dentro del operador de enlace, ya que su tipo especifica:

(>>=) :: m a -> (a -> m b) -> m b

La función 'ejecutar' para su mónada generalmente también puede hacer esto (para devolver un valor puro de su cálculo).

  

el objetivo de las mónadas es 'envolver' los efectos secundarios para que estén aislados del resto del programa

Hmm. No, las mónadas nos permiten modelar nociones de cálculo. Los cálculos de efectos secundarios son solo una de esas nociones, como es el estado, retroceso, continuaciones, concurrencia, transacciones, resultados opcionales, resultados aleatorios, estado revertable, no determinismo ... todo lo cual puede describirse como una mónada

Supongo que te refieres a la mónada IO. Es una mónada ligeramente extraña: genera secuencias de cambios abstractos en el estado del mundo, que luego son evaluados por el tiempo de ejecución. Bind simplemente nos permite secuenciar las cosas en el orden correcto en la mónada de E / S, y el compilador traducirá todas estas acciones secuenciales de modificación del mundo en un código imperativo que cambia ese estado de la máquina.

Sin embargo, eso es muy específico de la mónada IO, no de las mónadas en general.

La siguiente es la definición de la clase de tipo. Monad.

class  Monad m  where

    (>>=)       :: forall a b. m a -> (a -> m b) -> m b
    (>>)        :: forall a b. m a -> m b -> m b
    return      :: a -> m a
    fail        :: String -> m a

    m >> k      = m >>= \_ -> k
    fail s      = error s

Cada instancia de tipo de clase de tipo Monad define su propio >>= función.Aquí hay un ejemplo de la instancia de tipo. Maybe:

instance  Monad Maybe  where

    (Just x) >>= k      = k x
    Nothing  >>= _      = Nothing

    (Just _) >>  k      = k
    Nothing  >>  _      = Nothing

    return              = Just
    fail _              = Nothing

Como podemos ver, porque el Maybe versión de >>= está especialmente definido para entender el Maybe instancia de tipo, y porque está definida en un lugar que tiene acceso legal a la data Maybe a constructores de datos Nothing y Just a, el Maybe versión de >>= es capaz de desenvolver el a'pecado Maybe a y pasarlos.

Para trabajar con un ejemplo, podríamos tomar:

x :: Maybe Integer
x = do a <- Just 5
       b <- Just (a + 1)
       return b

Sin azúcar, la notación do se convierte en:

x :: Maybe Integer
x = Just 5        >>= \a ->
    Just (a + 1)  >>= \b ->
    Just b

Que se evalúa como:

  =                  (\a ->
    Just (a + 1)  >>= \b ->
    Just b) 5

  = Just (5 + 1)  >>= \b ->
    Just b

  =                  (\b ->
    Just b) (5 + 1)

  = Just (5 + 1)

  = Just 6

Los tipos se alinean, curiosamente. He aquí cómo.

Recuerda que una mónada también es functor. La siguiente función está definida para todos los functores:

fmap :: (Functor f) => (a -> b) -> f a -> f b

Ahora la pregunta: ¿realmente se alinean estos tipos? Bueno, sí. Dada una función de a a b, entonces si tenemos un entorno f en el que m está disponible, tenemos un entorno m a en el que m (m a) está disponible.

Por analogía con el silogismo:

(Functor Socrates) => (Man -> Mortal) -> Socrates Man -> Socrates Mortal

Ahora, como saben, una mónada es un functor equipado con bind y return:

return :: (Monad m) => a -> m a
(=<<) :: (Monad m) => (a -> m b) -> m a -> m b

Puede que no sepa que, de manera equivalente, es un functor equipado con return and join:

join :: (Monad m) => m (m a) -> m a

Vea cómo estamos despegando un (=<<). Con una mónada (a -> m b), no siempre puede pasar de fmap a m a -> m (m b), pero siempre puede ir de a -> m b a m (m b).

Ahora mira el primer argumento para join. Es una función de tipo <=>. ¿Qué sucede cuando pasa esa función a <=>? Obtienes <=>. Entonces, & Quot; mapeo & Quot; sobre un <=> con una función <=> le da <=>. Tenga en cuenta que esto es exactamente como el tipo de argumento para <=>. Esto no es una coincidencia. Una implementación razonable de & Quot; bind & Quot; se ve así:

(>>=) :: m a -> (a -> m b) -> m b
x >>= f = join (fmap f x)

De hecho, enlazar y unir se pueden definir en términos mutuos:

join = (>>= id)

RECOMIENDO MUCHO que lea ( http://blog.sigfpe.com/2006/08/you-could-have-invented-monads-and.html ). Da una razón perfecta y de sentido común por la que existen las mónadas.

  

Entiendo (creo) que el objetivo de las mónadas es 'envolver' los efectos secundarios para que estén aislados del resto del programa.

En realidad es un poco más sutil que eso. Las mónadas nos permiten modelar la secuencia de una manera muy general. A menudo, cuando hablas con un experto en el dominio, encuentras que dicen algo como & "; Primero intentamos con X. Luego intentamos con Y, y si eso no funciona, entonces intentamos con Z &"; Cuando venga a implementar algo así en un lenguaje convencional, encontrará que no encaja, por lo que debe escribir un montón de código adicional para cubrir lo que sea que el experto en el dominio quiso decir con la palabra & Quot; luego & Quot ;.

En Haskell puedes implementar esto como una mónada con " luego " traducido al operador de enlace. Entonces, por ejemplo, una vez escribí un programa en el que un elemento tenía que asignarse desde grupos de acuerdo con ciertas reglas. Para el caso 1, lo tomó del grupo X. Si eso estaba vacío, pasó al grupo Y. Para el caso 2, tuvo que tomarlo directamente del grupo Y. Y así durante una docena de casos, incluidos algunos donde tomó el menos utilizado recientemente desde el grupo X o Y. Escribí una mónada personalizada especialmente para ese trabajo para poder escribir:

case c of
   1: do {try poolX; try poolY}
   2: try poolY
   3: try $ lru [poolX, poolY]

Funcionó muy bien.

Por supuesto, esto incluye modelos convencionales de secuenciación. El IO monad es el modelo que tienen todos los demás lenguajes de programación; es solo que en Haskell es una elección explícita en lugar de parte del medio ambiente. La mónada ST le proporciona la mutación de memoria de IO, pero sin la entrada y salida reales. Por otro lado, la mónada State le permite restringir su estado a un solo valor de un tipo con nombre.

Para algo realmente emocionante, vea esta publicación de blog sobre una mónada del estado atrasada. El estado se propaga en la dirección opuesta a & Quot; ejecución & Quot ;. Si piensa en esto como una mónada de estado que ejecuta una instrucción seguida de la siguiente, entonces un & "; Ponga &"; enviará el valor del estado hacia atrás en el tiempo a cualquier precedente " get " ;. Lo que realmente sucede es que se configura una función recursiva mutua que solo termina si no hay paradojas. No estoy muy seguro de dónde usar una mónada, pero ilustra el punto de que las mónadas son modelos de cálculo.

Si no está preparado para eso, piense en bind como un punto y coma sobrecargable. Eso te lleva bastante lejos.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top