簡體   English   中英

為什么不能有一個 MonadFix 的實例作為 continuation monad?

[英]Why can't there be an instance of MonadFix for the continuation monad?

我們如何證明延續 monad沒有有效的MonadFix實例?

嗯,實際上,並不是不能有MonadFix實例,只是庫的類型有點太受限制了。 如果您在所有可能的r定義ContT ,那么不僅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 )

考慮延續 monad 的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值作為輸入。 但是,沒有辦法召喚 a 類型a值。 因此,對於延續 monad,沒有純粹的mfix居民。

請注意,您也不能遞歸定義mfix ,因為mfix fk = mfix ? ? mfix fk = mfix ? ? 由於沒有基本情況,將導致無限回歸。 而且,我們不能定義mfix fk = f ? ? mfix fk = f ? ? mfix fk = k ? 因為即使使用遞歸,也無法調用 a 類型a值。

但是,我們是否可以為 continuation monad 使用不純的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應用於x' 通常,我們會使用遞歸 let 表達式來執行此操作,即let x' = f x' 但是, x'不是f的返回值。 相反,給予f的延續應用於x' 為了解決這個難題,我們創建了一個空的可變變量m ,懶惰地讀取它的值x ,並將f應用於x 這樣做是安全的,因為f的參數不能嚴格。 f最終調用給它的延續時,我們將結果x'存儲在m並將延續k應用於x' 因此,當我們最終評估x我們得到結果x'

mfix延續 monad 的mfix實現看起來mfixIO monad 的mfix實現。

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操作上無限期地阻塞。

然而,只存儲第一個回調的結果似乎有點武斷。 所以,這里有一個mfix的實現,用於 continuation monad,它允許多次調用提供的 continuation。 我用 JavaScript 編寫它,因為我無法讓它在 Haskell 的惰性下很好地發揮作用。

 // mfix :: (Thunk a -> ContT rma) -> ContT rma 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的實現。

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

請注意,這看起來很像列表 monad。

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

這是有道理的,因為畢竟 continuation monad 是所有 monad 之母 當我離開的驗證MonadFix我的JavaScript執行的法律mfix作為練習留給讀者。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM