Bueno, fmap
es solo (a -> b) -> f a -> f b
, es decir, queremos transformar el resultado de la acción monádica con una función pura. Eso es fácil de escribir con la notación do:
fmap f m = do
a <- m
return (f a)
o, por escrito "en bruto":
fmap f m = m >>= \a -> return (f a)
Esto está disponible como Control.Monad.liftM
.
pure :: a -> f a
es por supuesto return
. (<*>) :: f (a -> b) -> f a -> f b
es un poco más complicado. Tenemos una acción que devuelve una función y una acción que devuelve su argumento, y queremos que una acción devuelva su resultado. En la notación do de nuevo:
mf <*> mx = do
f <- mf
x <- mx
return (f x)
O Desazucarado:
mf <*> mx =
mf >>= \f ->
mx >>= \x ->
return (f x)
Tada! Esto está disponible como Control.Monad.ap
, por lo que puede dar una instancia completa de Functor
y Applicative
para cualquier mónada M
de la siguiente manera:
instance Functor M where
fmap = liftM
instance Applicative M where
pure = return
(<*>) = ap
Idealmente, nos gustaría poder especificar estas implementaciones directamente en Monad
, para aliviar la carga de definir instancias separadas para cada mónada, como con this proposal. Si eso sucede, no habrá ningún obstáculo real para hacer que Applicative
sea una superclase de Monad
, ya que garantizará que no rompa ningún código existente. Por otro lado, esto significa que la repetición implicada en la definición de instancias Functor
y Applicative
para un Monad
dado es mínima, por lo que es fácil ser un "buen ciudadano" (y tales instancias deben definirse para cualquier mónada).
Esta respuesta falta una pieza importante: prueba de que si una 'instancia' Monad 'dada' m' en verdad satisface las leyes Mónadas, entonces las definiciones monádicas que usted proporciona para 'fmap',' pure' y '(<*>)' obedezca el Functor y las leyes Aplicativas. Todo lo que Haskell impone es que los tipos lo comprueben. –