从这个线程 (Control.Monad.Cont fun, 2005),Tomasz Zielonka 引入了一个函数(Thomas Jäger 以清晰而优美的方式评论)。Tomasz 接受 callCC 主体的参数(一个函数)并将其返回以供以后使用,具有以下两个定义:
import Control.Monad.Cont
...
getCC :: MonadCont m => m (m a)
getCC = callCC (\c -> let x = c x in return x)
getCC' :: MonadCont m => a -> m (a, a -> m b)
getCC' x0 = callCC (\c -> let f x = c (x, f) in return (x0, f))
Haskellwiki中也提到了这些。使用它们,您可以类似于 haskell 中的 goto 语义,看起来非常酷:
import Control.Monad.Cont
getCC' :: MonadCont m => a -> m (a, a -> m b)
getCC' x0 = callCC (\c -> let f x = c (x, f) in return (x0, f))
main :: IO ()
main = (`runContT` return) $ do
(x, loopBack) <- getCC' 0
lift (print x)
when (x < 10) (loopBack (x + 1))
lift (putStrLn "finish")
这将打印数字 0 到 10。
有趣的地方来了。我将它与 Writer Monad 一起使用来解决某个问题。我的代码如下所示:
{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, UndecidableInstances #-}
import Control.Monad.Cont
import Control.Monad.Writer
getCC :: MonadCont m => m (m a)
getCC = callCC (\c -> let x = c x in return x)
getCC' :: MonadCont m => a -> m (a, a -> m b)
getCC' x0 = callCC (\c -> let f x = c (x, f) in return (x0, f))
-- a simple monad transformer stack involving MonadCont and MonadWriter
type APP= WriterT [String] (ContT () IO)
runAPP :: APP a -> IO ()
runAPP a= runContT (runWriterT a) process
where process (_,w)= do
putStrLn $ unlines w
return ()
driver :: Int -> APP ()
driver k = do
tell [ "The quick brown fox ..." ]
(x,loop) <- getCC' 0
collect x
when (x<k) $ loop (x+1)
collect :: Int -> APP ()
collect n= tell [ (show n) ]
main :: IO ()
main = do
runAPP $ driver 4
当您编译并运行此代码时,输出为:
The quick brown fox ...
4
数字 0 到 3 在这个例子的深邃黑暗中被吞没了。
现在,在“Real World Haskell”中,奥沙利文、戈尔岑和斯图尔特状态
“堆叠 monad 转换器类似于组合函数。如果我们改变应用函数的顺序,然后得到不同的结果,我们不会感到惊讶。monad 转换器也是如此。” (真实世界的 Haskell,2008 年,第 442 页)
我想出了交换上面的变压器的想法:
--replace in the above example
type APP= ContT () (WriterT [String] IO)
...
runAPP a = do
(_,w) <- runWriterT $ runContT a (return . const ())
putStrLn $ unlines w
但是,这不会编译,因为在 Control.Monad.Cont 中没有 MonadWriter 的实例定义(这就是我最近问这个问题的原因。)
我们添加一个实例,离开监听并传递未定义:
instance (MonadWriter w m) => MonadWriter w (ContT r m) where
tell = lift . tell
listen = undefined
pass = undefined
添加这些行,编译并运行。所有数字都打印出来。
前面的例子发生了什么?