Аппликационный экземпляр для бесплатной монады
При попытке найти монашескую монаду, которая может быть выполнена ступенчато/разрешает потоки, я обнаружил свободную монаду
data Free f a = Return a | Roll (f (Free f a))
с его экземпляром монады
instance (Functor f) => Monad (Free f) where
return = Return
Return x >>= f = f x
Roll action >>= f = Roll $ fmap (>>= f) action
и его экземпляр функтора
instance (Functor f) => Functor (Free f) where
fmap f (Return x) = Return (f x)
fmap f (Roll x) = Roll $ fmap (fmap f) x
Я знаю, что каждая монада является аппликативным функтором с pure = return
и (<*>) = ap
.
Для меня аппликативные функторы концептуально сложнее, чем монады. Для лучшего понимания аппликативных функторов мне нравится иметь аппликативный экземпляр, не прибегая к ap
.
Первая строка для <*>
проста:
instance (Applicative f) => Applicative (Free f) where
pure = Return
Return f <*> x = fmap f x -- follows immediately from pure f <*> x = f <$> x
--Roll f <*> x = Roll $ (fmap ((fmap f) <*>)) x -- wrong, does not type-check
Как определить Roll f <*> x
в базовых терминах с помощью fmap
и <*>
?
Ответы
Ответ 1
Будет ли это делать?
instance (Functor f) => Applicative (Free f) where
pure = Return
Return f <*> as = fmap f as
Roll faf <*> as = Roll (fmap (<*> as) faf)
План должен действовать только на листьях дерева, который производит функцию, поэтому для Return
мы
действовать, применяя функцию ко всем значениям аргументов, созданным действием аргумента. Для Roll
мы просто делаем все под-действия, которые мы намереваемся сделать для общего действия.
Реально, что мы делаем, когда достигаем Return
, уже задано до начала. Мы не изменяем наши планы в зависимости от того, где мы находимся на дереве. Это признак Applicative
: структура вычисления фиксирована, поэтому значения зависят от значений, но действий нет.