Cómo usar (->) instancias de Monad y confusión sobre (->)

En diferentes preguntas, he encontrado sugerencias en los comentarios sobre el uso de la (->) instancia de Mónadas, por ejemplo, para realizar un estilo sin puntos.

En cuanto a mí, esto es un poco demasiado abstracto. Bien, he visto instancias de Arrow en (->) y me parece que (->) se puede usar en notaciones de instancias pero no en declaraciones de tipos (eso solo sería lo mismo para otra pregunta).

¿Alguien tiene ejemplos de usar (->) como instancia de Monad? O un buen enlace?

Disculpe si esta pregunta ya se debatió aquí, pero buscar (->) instancia de Monad” le da muchos hits como se puede imaginar … ya que casi todas las preguntas sobre Haskell implican (->) o “Monad” .

Para un tipo dado r , la función de tipo r -> a puede considerarse como un cálculo que entrega un a usando un entorno tipeado r . Dadas dos funciones r -> a y a -> (r -> b) , es fácil imaginar que uno puede componer estos cuando se le da un entorno (una vez más, de tipo r ).

¡Pero espera! ¡Eso es exactamente de lo que se trata la mónada!

Entonces podemos crear una instancia de Mónada para (->) r que implemente f >>= g pasando el r a ambos f y g . Esto es lo que hace la instancia de Monad para (->) r .

Para acceder realmente al entorno, puede usar id :: r -> r , que ahora puede pensar como un cálculo que se ejecuta en un entorno r y que entrega una r . Para crear subentornos locales, puede usar lo siguiente:

 inLocalEnvironment :: (r -> r) -> (r -> a) -> (r -> a) inLocalEnvironment xform f = \env -> f (xform env) 

Este patrón de tener un entorno pasado a computaciones que luego puede consultarlo y modificarlo localmente es útil no solo para la mónada (->) r , que es la razón por la cual se abstrae en la clase MonadReader , usando nombres mucho más sensibles que los que yo lo he usado aquí:

http://hackage.haskell.org/packages/archive/mtl/2.0.1.0/doc/html/Control-Monad-Reader-Class.html

Básicamente, tiene dos instancias: (->) r que hemos visto aquí, y ReaderT rm , que es simplemente un envoltorio newtype alrededor de r -> ma , entonces es lo mismo que la (->) r mónada I ‘ Aquí se describe, excepto que proporciona cálculos en alguna otra mónada transformada.

Para definir una mónada para (->) r , necesitamos dos operaciones, return y (>>=) , sujetas a tres leyes:

 instance Monad ((->) r) where 

Si miramos la firma de retorno para (->) r

  return :: a -> r -> a 

podemos ver que es solo la función constante, que ignora su segundo argumento.

  return ar = a 

O alternativamente,

  return = const 

Para comstackr (>>=) , si especializamos su firma de tipo con la mónada (->) r ,

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

en realidad solo hay una definición posible.

  (>>=) xyz = y (xz) z 

Usar esta mónada es como transmitir un argumento adicional r a cada función. Puede usar esto para configuración, o para pasar opciones muy abajo en las entrañas de su progtwig.

Podemos verificar que sea una mónada, verificando las tres leyes de la mónada:

 1. return a >>= f = fa return a >>= f = (\b -> a) >>= f -- by definition of return = (\xyz -> y (xz) z) (\b -> a) f -- by definition of (>>=) = (\yz -> y ((\b -> a) z) z) f -- beta reduction = (\z -> f ((\b -> a) z) z) -- beta reduction = (\z -> faz) -- beta reduction = fa -- eta reduction 2. m >>= return = m m >>= return = (\xyz -> y (xz) z) m return -- definition of (>>=) = (\yz -> y (mz) z) return -- beta reduction = (\z -> return (mz) z) -- beta reduction = (\z -> const (mz) z) -- definition of return = (\z -> mz) -- definition of const = m -- eta reduction 

La ley de la mónada final:

 3. (m >>= f) >>= g ≡ m >>= (\x -> fx >>= g) 

sigue por un razonamiento ecuacional similar y fácil.

Podemos definir un número de otras clases para ((->) r) también, como Functor,

 instance Functor ((->) r) where 

y si miramos la firma de

  -- fmap :: (a -> b) -> (r -> a) -> r -> b 

¡podemos ver que es solo una composición!

  fmap = (.) 

Del mismo modo, podemos hacer una instancia de Applicative

 instance Applicative ((->) r) where -- pure :: a -> r -> a pure = const -- (<*>) :: (r -> a -> b) -> (r -> a) -> r -> b (<*>) gfr = gr (fr) 

Lo bueno de tener estas instancias es que te permiten emplear todos los combinadores de Monad y Applicative al manipular funciones.

Hay muchas instancias de clases que implican (->), por ejemplo, puede escribir a mano la instancia de Monoid para (b -> a), dado un Monoid en a como:

 enter code here instance Monoid a => Monoid (b -> a) where -- mempty :: Monoid a => b -> a mempty _ = mempty -- mappend :: Monoid a => (b -> a) -> (b -> a) -> b -> a mappend fgb = fb `mappend` gb 

pero dada la instancia de Monad / Applicative, también puede definir esta instancia con

 instance Monoid a => Monoid (r -> a) where mempty = pure mempty mappend = liftA2 mappend 

utilizando la instancia Aplicable para (->) r o con

 instance Monoid a => Monoid (r -> a) where mempty = return mempty mappend = liftM2 mappend 

usando la instancia de Monad para (->) r .

Aquí los ahorros son mínimos, pero, por ejemplo, la herramienta @pl para generar código sin puntos, que es proporcionada por lambdabot en el canal IRC #haskell, abusa de estas instancias bastante.

    Intereting Posts