Подтвердить что ты не робот

Аппликационный экземпляр для бесплатной монады

При попытке найти монашескую монаду, которая может быть выполнена ступенчато/разрешает потоки, я обнаружил свободную монаду

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 и <*>?

4b9b3361

Ответ 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: структура вычисления фиксирована, поэтому значения зависят от значений, но действий нет.