haskell functional-programming monads pointfree

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



functional-programming monads (2)

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 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 a r = a

O alternativamente,

return = const

Para compilar (>>=) , 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.

(>>=) x y z = y (x z) 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 programa.

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

1. return a >>= f = f a return a >>= f = (/b -> a) >>= f -- by definition of return = (/x y z -> y (x z) z) (/b -> a) f -- by definition of (>>=) = (/y z -> y ((/b -> a) z) z) f -- beta reduction = (/z -> f ((/b -> a) z) z) -- beta reduction = (/z -> f a z) -- beta reduction = f a -- eta reduction 2. m >>= return = m m >>= return = (/x y z -> y (x z) z) m return -- definition of (>>=) = (/y z -> y (m z) z) return -- beta reduction = (/z -> return (m z) z) -- beta reduction = (/z -> const (m z) z) -- definition of return = (/z -> m z) -- definition of const = m -- eta reduction

La ley de la mónada final:

3. (m >>= f) >>= g ≡ m >>= (/x -> f x >>= 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 (<*>) g f r = g r (f r)

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 f g b = f b `mappend` g b

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.


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.