Возможно ли реализовать 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 будет уменьшен.