mgi*_*uca 9 monads continuations haskell
令人困惑的问题令人困惑的标题!我理解a)monad,b)IO monad,c)Cont monad(Control.Monad.Cont),以及d)ContT连续变换器monad.(而且我模糊地理解monad变换器 - 虽然还不足以回答这个问题.)我理解如何编写一个程序,其中所有函数都在Cont monad(Cont r a
)中,我理解如何编写一个程序,其中所有的函数在组合的Cont/IO monad(ContT r IO a
)中.
但我想知道如何编写一个程序,其中某些函数在组合的Cont/IO monad(ContT r IO a
)中,其他函数只在Cont monad(Cont r a
)中.基本上,我想以连续样式编写整个程序,但只在必要时使用IO monad(很像"常规"Haskell代码,我只在必要时使用IO monad).
例如,以非延续样式考虑这两个函数:
foo :: Int -> IO Int
foo n = do
let x = n + 1
print x
return $ bar x
bar :: Int -> Int
bar m = m * 2
Run Code Online (Sandbox Code Playgroud)
请注意,foo
需要IO但是bar
纯粹.现在我想出了如何使用continuation monad完全编写这段代码,但我还需要通过IO连接bar
:
foo :: Int -> ContT r IO Int
foo n = do
let x = n + 1
liftIO $ print x
bar x
bar :: Int -> ContT r IO Int
bar m = return $ m * 2
Run Code Online (Sandbox Code Playgroud)
我确实希望我的所有代码都是延续式的,但我不想在不需要它的函数上使用IO monad.基本上,我想这样定义bar
:
bar :: Int -> Cont r Int
bar m = return $ m * 2
Run Code Online (Sandbox Code Playgroud)
不幸的是,我找不到从Cont r a
monad函数(bar
)内部调用ContT r IO a
monad函数(foo
)的方法.有没有办法将一个未经改造的monad"提升"为一个变形的monad?即,如何更改" bar x
" 行,foo
以便它可以正确调用bar :: Int -> Cont r Int
?
Car*_*arl 17
这就是Control.Monad.Class的用武之地.bar
在它可以使用的monad中创建多态:
bar :: MonadCont m => Int -> m Int
bar m = return $ m * 2
Run Code Online (Sandbox Code Playgroud)
请注意,页面底部的实例列表显示MonadCont
生成文档时已知的实例包括Cont r
和Monad m => ContT r m
.此外,MonadCont
该类定义了callCC
函数,这是使用延续特性所必需的.这意味着您可以在其中使用延续的完整表现力bar
,即使此示例没有.
通过这种方式,您可以编写可证明不能使用IO的函数,因为它们没有MonadIO
约束,也没有明确提及它们的类型IO
.但是它们是多态的,其中monad在其中工作,因此它们可以从包含IO的上下文中简单地调用.
我发现这完全符合我的要求(无需更改Bar
):
liftCont :: Cont (m r) a -> ContT r m a
liftCont = ContT . runCont
Run Code Online (Sandbox Code Playgroud)
这解压Cont
并构造了一个ContT
.
然后我可以使用liftCont
呼叫Bar
来自Foo
:
foo n = do
let x = n + 1
liftIO $ print x
liftCont $ bar x
Run Code Online (Sandbox Code Playgroud)
我不认为这比卡尔的解决方案"更好"(我给他打勾),但我在这里发布它是因为它允许你使用Bar
而不修改它的类型,如果你不能修改那么有用Bar
.(虽然它的表现可能更差.)