为什么MonadFix的实例不能用于延续monad?

时间:2014-09-13 20:10:47

标签: haskell monads continuation monadfix

我们如何证明the continuation monad没有MonadFix的有效实例?

2 个答案:

答案 0 :(得分:4)

实际上,并不是说不能成为MonadFix实例,只是库的类型有点过于局限。如果您对所有可能的ContT定义r,那么MonadFix不仅可以成为可能,而且所有Monad的实例都不需要底层仿函数:

newtype ContT m a = ContT { runContT :: forall r. (a -> m r) -> m r }
instance Functor (ContT m) where
  fmap f (ContT k) = ContT (\kb -> k (kb . f))
instance Monad (ContT m) where
  return a = ContT ($a)
  join (ContT kk) = ContT (\ka -> kk (\(ContT k) -> k ka))
instance MonadFix m => MonadFix (ContT m) where
  mfix f = ContT (\ka -> mfixing (\a -> runContT (f a) ka<&>(,a)))
    where mfixing f = fst <$> mfix (\ ~(_,a) -> f a )

答案 1 :(得分:2)

请考虑mfix的类型签名作为延续单子。

(a -> ContT r m a) -> ContT r m a

-- expand the newtype

(a -> (a -> m r) -> m r) -> (a -> m r) -> m r

这是没有纯居民的证明。

---------------------------------------------
(a -> (a -> m r) -> m r) -> (a -> m r) -> m r

introduce f, k

f :: a -> (a -> m r) -> m r
k :: a -> m r
---------------------------
m r

apply k

f :: a -> (a -> m r) -> m r
k :: a -> m r
---------------------------
a

dead end, backtrack

f :: a -> (a -> m r) -> m r
k :: a -> m r
---------------------------
m r

apply f

f :: a -> (a -> m r) -> m r     f :: a -> (a -> m r) -> m r
k :: a -> m r                   k :: a -> m r
---------------------------     ---------------------------
a                               a -> m r

dead end                        reflexivity k

如您所见,问题在于fk都希望输入a类型的值。但是,没有办法来产生类型a的值。因此,没有mfix的纯居民是延续单子。

请注意,您也不能递归定义{{​​1}},因为mfix会导致无穷大的回归,因为没有基本情况。而且,我们无法定义mfix f k = mfix ? ?mfix f k = f ? ?,因为即使进行递归,也无法产生mfix f k = k ?类型的值。

但是,对于接续单子,我们是否可以使用a的不正确实现?请考虑以下内容。

mfix

出现的问题是如何将import Control.Concurrent.MVar import Control.Monad.Cont import Control.Monad.Fix import System.IO.Unsafe instance MonadFix (ContT r m) where mfix f = ContT $ \k -> unsafePerformIO $ do m <- newEmptyMVar x <- unsafeInterleaveIO (readMVar m) return . runContT (f x) $ \x' -> unsafePerformIO $ do putMVar m x' return (k x') 应用于f。通常,我们将使用递归let表达式,即x'进行此操作。但是,let x' = f x'不是x'的返回值。相反,给f的延续被应用于f。为了解决这个难题,我们创建了一个空的可变变量x',延迟读取其值m,并将x应用于f。这样做是安全的,因为x的参数一定不能严格。当f最终调用给定的延续时,我们将结果f存储在x'中,并将延续m应用于k。因此,当我们最终评估x'时,我们得到的结果x

上述x'对延续单子的实现非常类似于mfixmfix单子的实现。

IO

请注意,在import Control.Concurrent.MVar import Control.Monad.Fix instance MonadFix IO where mfix f = do m <- newEmptyMVar x <- unsafeInterleaveIO (takeMVar m) x' <- f x putMVar m x' return x' 的实现中,我们继续使用mfix,而在readMVar的实现中,我们将{{1} }。这是因为赋予mfix的延续可以被多次调用。但是,我们只想存储提供给第一个回调的结果。使用IO代替takeMVar可确保可变变量保持为满。因此,如果连续调用不止一次,则第二个回调将在f操作中无限期地阻塞。

但是,仅存储第一个回调的结果似乎是任意的。因此,这是readMVar的延续monad的实现,该实现允许多次调用提供的延续。我之所以用JavaScript编写代码,是因为在Haskell中无法让它与懒惰一起很好地玩。

takeMVar

这里是等效的Haskell代码,没有putMVar的实现。

mfix

请注意,这看起来很像列表单子。

// mfix :: (Thunk a -> ContT r m a) -> ContT r m a
const mfix = f => k => {
    const ys = [];

    return (function iteration(n) {
        let i = 0, x;

        return f(() => {
            if (i > n) return x;
            throw new ReferenceError("x is not defined");
        })(y => {
            const j = i++;

            if (j === n) {
                ys[j] = k(x = y);
                iteration(i);
            }

            return ys[j];
        });
    }(0));
};

const example = triple => k => [
    { a: () => 1, b: () => 2, c: () => triple().a() + triple().b() },
    { a: () => 2, b: () => triple().c() - triple().a(), c: () => 5 },
    { a: () => triple().c() - triple().b(), b: () => 5, c: () => 8 },
].flatMap(k);

const result = mfix(example)(({ a, b, c }) => [{ a: a(), b: b(), c: c() }]);

console.log(result);

这很有意义,因为毕竟延续monad是the mother of all monads。我将保留对mfix的JavaScript实现的import Control.Monad.Cont import Control.Monad.Fix data Triple = { a :: Int, b :: Int, c :: Int } deriving Show example :: Triple -> ContT r [] Triple example triple = ContT $ \k -> [ Triple 1 2 (a triple + b triple) , Triple 2 (c triple - a triple) 5 , Triple (c triple - b triple) 5 8 ] >>= k result :: [Triple] result = runContT (mfix example) pure main :: IO () main = print result 定律的验证,作为读者的练习。