Ответ 1
Ну, fmap
- это просто (a -> b) -> f a -> f b
, то есть мы хотим преобразовать результат монадического действия с чистой функцией. Это легко написать с обозначением:
fmap f m = do
a <- m
return (f a)
или, написано "raw":
fmap f m = m >>= \a -> return (f a)
Это доступно как Control.Monad.liftM
.
pure :: a -> f a
, конечно, return
. (<*>) :: f (a -> b) -> f a -> f b
немного сложнее. У нас есть действие, возвращающее функцию, и действие, возвращающее его аргумент, и мы хотим, чтобы действие возвращало его результат. В повторной нотации:
mf <*> mx = do
f <- mf
x <- mx
return (f x)
Или, desugared:
mf <*> mx =
mf >>= \f ->
mx >>= \x ->
return (f x)
Тада! Это доступно как Control.Monad.ap
, поэтому мы можем предоставить полный экземпляр Functor
и Applicative
для любой монады M
как следующим образом:
instance Functor M where
fmap = liftM
instance Applicative M where
pure = return
(<*>) = ap
В идеале мы могли бы указать эти реализации непосредственно в Monad
, чтобы облегчить определение отдельных экземпляров для каждой монады, например это предложение. Если это произойдет, не будет реального препятствия для создания Applicative
суперкласса Monad
, так как это гарантирует, что он не нарушит существующий код. С другой стороны, это означает, что шаблон, участвующий в определении экземпляров Functor
и Applicative
для данного Monad
, минимален, поэтому легко быть "хорошим гражданином" (и такие экземпляры должны быть определены для любой монады).