このスレッド(Control.Monad.Cont fun、2005)から、Tomasz Zielonkaが関数を導入しました(ThomasJägerによって明確で素晴らしい方法でコメントされています)。Tomaszは、callCC本体の引数(関数)を受け取り、次の2つの定義を使用して後で使用できるように返します。
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の数値が出力されます。
ここに興味深い点があります。これをライターモナドと一緒に使って、ある問題を解決しました。私のコードは次のようになります。
{-# 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」では、オサリバン、ゲルゼン、スチュワートの各州が
「モナド変換子の積み重ねは、関数の作成に似ています。関数を適用する順序を変更して、異なる結果が得られても、驚くことはありません。したがって、モナド変換子も同様です。」(Real World Haskell、2008、P.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
それらの行を追加し、コンパイルして実行します。すべての数字が印刷されます。
前の例で何が起こったのですか?