首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么不能有一个MonadFix的实例用于延续monad呢?

为什么不能有一个MonadFix的实例用于延续monad呢?
EN

Stack Overflow用户
提问于 2014-09-14 04:10:47
回答 2查看 1.3K关注 0票数 20

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

EN

回答 2

Stack Overflow用户

发布于 2014-09-15 07:29:55

实际上,这并不是说不能有一个MonadFix实例,只是库的类型有点太约束了。如果您在所有可能的r上定义ContT,那么不仅MonadFix成为可能,而且Monad之前的所有实例都不需要底层函数器:

代码语言:javascript
复制
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 )
票数 6
EN

Stack Overflow用户

发布于 2020-09-16 00:31:16

考虑一下延续单体的mfix的类型签名。

代码语言:javascript
复制
(a -> ContT r m a) -> ContT r m a

-- expand the newtype

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

这就是没有这种类型的纯居民的证据。

代码语言:javascript
复制
---------------------------------------------
(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类型的值。因此,对于延续monad,没有纯粹的mfix居民。

请注意,您也不能递归地定义mfix,因为mfix f k = mfix ? ?会导致无限回归,因为没有基本情况。而且,我们不能定义mfix f k = f ? ?mfix f k = k ?,因为即使使用递归,也无法产生a类型的值。

但是,我们可以有一个不纯的mfix实现用于延续monad吗?请考虑以下内容。

代码语言:javascript
复制
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应用于x'。通常,我们会使用一个递归的let表达式,即let x' = f x'。但是,x'不是f的返回值。取而代之的是,将f的延续应用于x'。为了解决这个难题,我们创建了一个空的可变变量m,懒洋洋地读取它的值x,然后将f应用于x。这样做是安全的,因为f的参数不能太严格。当f最终调用给定的延续时,我们将结果x'存储在m中,并将延续k应用于x'。因此,当我们最终计算x时,我们得到的结果是x'

上面的延续monad的mfix实现看起来很像IO monad的mfix实现。

代码语言:javascript
复制
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'

请注意,在为延续monad实现mfix时,我们使用了readMVar,而在为IO monad实现mfix时,我们使用了takeMVar。这是因为,提供给f的延续可以被多次调用。但是,我们只想存储给第一个回调的结果。使用readMVar而不是takeMVar可以确保可变变量保持为满。因此,如果连续被多次调用,那么第二个回调将在putMVar操作上无限期地阻塞。

然而,只存储第一个回调的结果似乎有点武断。因此,这里有一个用于multiple的mfix实现,它允许多次调用提供的multiple。我用JavaScript写的,因为我不能让它很好地处理Haskell中的懒惰。

代码语言:javascript
复制
// 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);

下面是等效的Haskell代码,其中没有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

请注意,这看起来很像list monad。

代码语言:javascript
复制
import Control.Monad.Fix

data Triple = { a :: Int, b :: Int, c :: Int } deriving Show

example :: Triple -> [Triple]
example triple =
    [ Triple 1 2 (a triple + b triple)
    , Triple 2 (c triple - a triple) 5
    , Triple (c triple - b triple) 5 8
    ]

result :: [Triple]
result = mfix example

main :: IO ()
main = print result

这是有意义的,因为毕竟延续单数是the mother of all monads。我将把对我的mfix的JavaScript实现的MonadFix规则的验证留给读者作为练习。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/25827227

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档