Возможно ли реализовать MonadFix для `Free`?
http://hackage.haskell.org/package/free в Control.Monad.Free.Free
позволяет получить доступ к "свободной монаде" для любого заданного Functor
. Однако он не имеет экземпляра MonadFix
. Это потому, что такой экземпляр не может быть написан, или он просто оставлен?
Если такой экземпляр не может быть написан, почему бы и нет?
Ответы
Ответ 1
Рассмотрим описание того, что mfix
делает:
Фиксированная точка монадического вычисления. mfix f
выполняет действие f
только один раз, а конечный выход возвращается в качестве входа.
Слово "исполняет" в контексте Free
означает создание слоев Functor
. Таким образом, "только один раз" означает, что в результате оценки mfix f
значения, содержащиеся в конструкторах Pure
, должны полностью определять, сколько слоев функтора создано.
Теперь, скажем, у нас есть определенная функция once
, которая, как мы знаем, всегда будет создавать только один конструктор Free
, но, тем не менее, многие конструкторы Pure
необходимы для хранения значений листа. Тогда вывод "один раз" будет только значениями типа Free f a
, которые изоморфны некоторому значению типа f a
. С помощью этих знаний мы можем безопасно удалять вывод once
, чтобы получить значение типа f a
.
Теперь обратите внимание, что поскольку mfix
требуется "выполнить действие только один раз", результат mfix once
должен для соответствующего экземпляра не содержать дополнительной монадической структуры, чем once
создает в одном приложении. Таким образом, можно вывести, что значение, полученное из mfix once
, должно быть также изоморфно значению типа f a
.
Для любой функции с типом a -> f a
для некоторого Functor
f
мы можем обернуть результат с помощью одного Free
и получить функцию типа a -> Free f a
, которая удовлетворяет описанию once
выше, и мы уже установили, что мы можем развернуть результат mfix once
, чтобы получить значение типа f a
назад.
Следовательно, соответствующий экземпляр (Functor f) => MonadFix (Free f)
подразумевает возможность записи посредством описанной выше обертки и развертывания функции ffix :: (Functor f) => (a -> f a) -> f a
, которая будет работать для всех экземпляров Functor
.
Это, очевидно, не доказательство того, что вы не можете написать такой экземпляр... но если бы это было возможно, MonadFix
было бы совершенно лишним, потому что вы могли бы так же легко написать ffix
напрямую. (И я полагаю, переопределить его как mfix
с ограничением Monad
, используя liftM
. Ugh.)
Ответ 2
Ну, вдохновленный экземпляром MonadFix
для Maybe
, я попробовал это (используя следующие определения Free
):
data Free f a
= Pure a
| Impure (f (Free f a))
instance (Functor f) => Monad (Free f) where
return = Pure
Pure x >>= f = f x
Impure x >>= f = Impure (fmap (>>= f) x)
instance (Functor f) => MonadFix (Free f) where
mfix f = let Pure x = f x in Pure x
Законы:
- Чистота:
mfix (return . h) = return (fix h)
- Левая усадка:
mfix (\x -> a >>= \y -> f x y) = a >>= \y -> mfix (\x -> f x y)
- Скольжение:
mfix (liftM h . f) = liftM h (mfix (f . h))
, для строгих h
- Вложение:
mfix (\x -> mfix (f x)) = mfix (\x -> f x x)
Чистота легко доказать - но я столкнулся с проблемой при попытке доказать укорачивание слева:
mfix (\x -> a >>= \y -> f x y)
= let Pure x = (\x -> a >>= \y -> f x y) x in Pure x
= let Pure x = a >>= \y -> f x y in Pure x
-- case a = Pure z
= let Pure x = Pure z >>= \y -> f x y in Pure x
= let Pure x = f x z in Pure x
= let Pure x = (\x -> f x z) x in Pure x
= mfix (\x -> f x z)
= Pure z >>= \y -> mfix (\x -> f x y)
-- case a = Impure t
= let Pure x = Impure t >>= \y -> f x y in Pure x
= let Pure x = Impure (fmap (>>= \y -> f x y) t) in Pure x
= Pure _|_
но
Impure t >>= \y -> mfix (\x -> f x y)
= Impure (fmap (>>= \y -> mfix (\x -> f x y)) t)
/= Pure _|_
Итак, по крайней мере, если конструкторы Pure
и Impure
различимы, то моя реализация mfix
не удовлетворяет законам. Я не могу думать о какой-либо другой реализации, но это не значит, что этого не существует. Извините, я не мог больше осветить.
Ответ 3
Нет, его нельзя написать вообще, потому что не каждая Монада является экземпляром MonadFix.
Каждая Монада может быть реализована с использованием FreeMonad. Если вы можете реализовать MonadFix бесплатно, то вы сможете получить MonadFix из любой Monad, что невозможно.
Но, конечно, вы можете определить FreeFix для класса MonadFix.
Я думаю, что это может выглядеть так или иначе, но это всего лишь 3-я догадка (все еще не проверена):
data FreeFix m a = FreeFix { runFreeFix :: (forall r. (r -> m r) -> m r) -> m a }
instance (Monad m) => Monad (FreeFix m) where
return a = FreeFix $ \_-> do
return a
f >>= g = FreeFix $ \mfx -> do
x <- runFreeFix f mfx
runFreeFix (g x) mfx
instance (Monad m) => MonadFix (FreeFix m) where
mfix f = FreeFix $ \mfx -> do
mfx (\r->runFreeFix (f r) mfx)
Идея состоит в том, что m является Monad, которому не хватает реализации для mfix; поэтому mfix должен быть параметром, когда FreeFix будет уменьшен.