[英]Why can't there be an instance of MonadFix for the continuation monad?
嗯,實際上,並不是不能有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
正如您所看到的,問題是f
和k
期望 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
實現看起來mfix
對IO
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.