you monad learn example haskell monads

haskell - learn - monad example



¿Cómo se compone una ap frombe? (4)

Disculpas por la respuesta lacónica y mecánica. No me gustan las cosas que me gusta elegir, como Applicative o Monad, pero no sé dónde estás. Este no es mi enfoque habitual para enseñar Haskell .

Primero, ap está realmente (<*>) debajo del capó.

Prelude> import Control.Monad Prelude> import Data.Maybe Prelude> import Control.Applicative Prelude> :t ap ap :: Monad m => m (a -> b) -> m a -> m b Prelude> :t (<*>) (<*>) :: Applicative f => f (a -> b) -> f a -> f b

¿Qué significa esto? Significa que no necesitamos algo tan "fuerte" como Monad para describir lo que estamos haciendo. Suficiente aplicativo. Sin embargo, Functor no.

Prelude> :info Applicative class Functor f => Applicative (f :: * -> *) where pure :: a -> f a (<*>) :: f (a -> b) -> f a -> f b Prelude> :info Functor class Functor (f :: * -> *) where fmap :: (a -> b) -> f a -> f b

Aquí está ap / (<*>) con el Tal vez Monad / Aplicativo:

Prelude> ap (Just (+1)) (Just 1) Just 2 Prelude> (<*>) (Just (+1)) (Just 1) Just 2

Lo primero que debemos averiguar es, ¿de qué instancia de la clase de Typus estamos hablando?

Prelude> :t fromMaybe fromMaybe :: a -> Maybe a -> a

Desugarse del tipo de Maybe un poco nos da:

(->) a (Maybe a -> a)

Así que el constructor de tipos que nos ocupa aquí es (->) . ¿Qué nos dice GHCi acerca de (->) también conocidos como tipos de funciones?

Prelude> :info (->) data (->) a b -- Defined in ‘GHC.Prim’ instance Monad ((->) r) -- Defined in ‘GHC.Base’ instance Functor ((->) r) -- Defined in ‘GHC.Base’ instance Applicative ((->) a) -- Defined in ‘GHC.Base’

Hrm ¿Qué tal tal vez?

Prelude> :info Maybe data Maybe a = Nothing | Just a -- Defined in ‘GHC.Base’ instance Monad Maybe -- Defined in ‘GHC.Base’ instance Functor Maybe -- Defined in ‘GHC.Base’ instance Applicative Maybe -- Defined in ‘GHC.Base’

Lo que sucedió con el uso de (<*>) para Maybe fue esto:

Prelude> (+1) 1 2 Prelude> (+1) `fmap` Just 1 Just 2 Prelude> Just (+1) <*> Just 1 Just 2 Prelude> :t fmap fmap :: Functor f => (a -> b) -> f a -> f b Prelude> let mFmap = fmap :: (a -> b) -> Maybe a -> Maybe b Prelude> (+1) `mFmap` Just 1 Just 2 Prelude> :t (<*>) (<*>) :: Applicative f => f (a -> b) -> f a -> f b Prelude> let mAp = (<*>) :: Maybe (a -> b) -> Maybe a -> Maybe b Prelude> :t (+1) (+1) :: Num a => a -> a Prelude> :t Just (+1) Just (+1) :: Num a => Maybe (a -> a) Prelude> Just (+1) `mAp` Just 1 Just 2

Bien, ¿qué pasa con el Functor y el Aplicativo del tipo de función? Una de las partes difíciles aquí es que (->) debe aplicarse parcialmente en el tipo para ser un Functor / Applicative / Monad. Entonces, f convierte en (->) a del (->) ab global donde a es un tipo de argumento y b es el resultado.

Prelude> (fmap (+1) (+2)) 0 3 Prelude> (fmap (+1) (+2)) 0 3 Prelude> :t fmap fmap :: Functor f => (a -> b) -> f a -> f b Prelude> let funcMap = fmap :: (a -> b) -> (c -> a) -> c -> b Prelude> -- f ~ (->) c Prelude> (funcMap (+1) (+2)) 0 3 Prelude> :t (<*>) (<*>) :: Applicative f => f (a -> b) -> f a -> f b Prelude> let funcAp = (<*>) :: (c -> a -> b) -> (c -> a) -> (c -> b) Prelude> :t fromMaybe fromMaybe :: a -> Maybe a -> a Prelude> :t funcAp fromMaybe funcAp fromMaybe :: (b -> Maybe b) -> b -> b Prelude> :t const const :: a -> b -> a Prelude> :t funcAp const funcAp const :: (b -> b1) -> b -> b

No se garantiza ser útil. Se puede decir que funcAp const no es interesante solo por el tipo y saber cómo funciona la parametricidad.

Edición: hablando de componer, el Functor para (->) a es solo (.) . Aplicativo es eso, pero con un argumento extra. La mónada es el aplicativo, pero con los argumentos invertidos.

Whuttery adicional: el aplicativo <*> para (->) a ) es S y pure es K del cálculo del combinador SKI. (Puede derivar I de K y S. En realidad, puede derivar cualquier programa de K y S.)

Prelude> :t pure pure :: Applicative f => a -> f a Prelude> :t const const :: a -> b -> a Prelude> :t const const :: a -> b -> a Prelude> let k = pure :: a -> b -> a Prelude> k 1 2 1 Prelude> const 1 2 1

Ahí estaba yo, escribiendo una función que toma un valor como entrada, llama a una función en esa entrada, y si el resultado es Just x , debería devolver x ; de lo contrario, debería devolver la entrada original.

En otras palabras, esta función (que no sabía cómo llamar):

foo :: (a -> Maybe a) -> a -> a foo f x = fromMaybe x (f x)

Ya que parece ser una función de propósito general, me pregunté si no estaba ya definida, así que pregunté en Twitter , y Chris Allen respondió que es una ap fromMaybe .

Eso sonaba prometedor, así que encendí GHCI y comencé a experimentar:

Prelude Control.Monad Data.Maybe> :type ap ap :: Monad m => m (a -> b) -> m a -> m b Prelude Control.Monad Data.Maybe> :type fromMaybe fromMaybe :: a -> Maybe a -> a Prelude Control.Monad Data.Maybe> :type ap fromMaybe ap fromMaybe :: (b -> Maybe b) -> b -> b

El tipo de ap fromMaybe parece ciertamente correcto, y un par de experimentos parecen indicar que también tiene el comportamiento deseado.

Pero, ¿cómo funciona?

La función fromMaybe me parece clara y, de forma aislada, creo que entiendo lo que hace ap , al menos en el contexto de Maybe . Cuando m es Maybe , tiene el tipo Maybe (a -> b) -> Maybe a -> Maybe b .

Lo que no entiendo es cómo una ap fromMaybe incluso compilar. Para mí, esta expresión parece una aplicación parcial, pero es posible que me esté equivocando. Si este es el caso, sin embargo, no entiendo cómo los tipos coinciden.

El primer argumento para ap es m (a -> b) , pero de fromMaybe tenga el tipo a -> Maybe a -> a . ¿Cómo encaja eso? ¿En qué instancia de Monad el compilador infiere que m es? ¿Cómo fromMaybe , que toma dos argumentos (al curry), se convierte en una función que toma un solo argumento?

¿Puede alguien ayudarme a conectar los puntos?


La mónada que estás buscando es (->) r o r -> _ si prefieres la sintaxis de infijo.

Entonces la firma de ap expande a:

m (a -> b) -> m a -> m b = (r -> (a -> b)) -> (r -> a) -> r -> b = -- now we use the signature of fromMaybe (b -> (Maybe b -> b)) -> (b -> Maybe b) -> b -> b

Ahora, si considera ap fromMaybe como una función parcialmente aplicada y voila obtendrá el resultado deseado.


Pero ese uso de ap no está en el contexto de Maybe . Lo estamos utilizando con una función, desde fromMaybe , por lo que está en el contexto de las funciones, donde

ap f g x = f x (g x)

Entre las diversas instancias de Monad tenemos

instance Monad ((->) r)

así es

ap :: Monad m => m (a -> b) -> m a -> m b fromMaybe :: r -> (Maybe r -> r) ap :: (r -> (a -> b)) -> (r -> a) -> (r -> b) ap f g x :: b ap fromMaybe :: (r -> a) -> (r -> b) , a ~ Maybe r , b ~ r

porque -> en tipos se asocia a la derecha: a -> b -> c ~ a -> (b -> c) . Tratando de conectar los tipos juntos, solo podemos terminar con la definición anterior.

Y con (<*>) :: Applicative f => f (a -> b) -> fa -> fb , podemos escribirlo como (fromMaybe <*>) , si te gusta este tipo de graffiti:

#> :t (fromMaybe <*>) (fromMaybe <*>) :: (r -> Maybe r) -> r -> r

Como se señala con razón en otra respuesta aquí, cuando se usa con funciones, <*> es simplemente su buen combinador ole '' S. No podemos tener una función llamada S en Haskell, así que <*> es solo una parte del repertorio estándar del estilo de codificación sin puntos. El enlace monádico (más aún, volteado), =<< , puede ser incluso más misterioso, pero a un programador de punto libre no le importa y lo usará felizmente para codificar otro patrón similar,

(f =<< g) x = f (g x) x

en llamadas de función combinatory , misterio o no misterio ( zipWith (-) =<< drop 1 viene a la mente).


Voy a volver a etiquetar los argumentos de tipo, para mayor claridad.

ap :: Monad m => m (a -> b) -> m a -> m b fromMaybe :: c -> Maybe c -> c

¿En qué instancia de Monad el compilador infiere que m es?

((->) r) es una Monad . Se trata de todas las funciones que tienen el tipo r como argumento, para algunas r específicas .

Así que en el tipo:

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

m ~ (c ->) , a ~ Maybe c y b ~ c .

El tipo de retorno, ma -> mb , se expande a (c -> Maybe c) -> c -> c - que es el tipo de ap fromMaybe .