| Prev: Writer モナド | TOC: 目次 | Next: Part III - イントロダクション |
| 計算のタイプ: | 中断,再開が可能な計算 |
|---|---|
| 束縛の戦略: | 関数をモナド値に束縛すると新しい継続が生成され,その継続は その関数をそのモナド計算の継続として使います. |
| 利用場面: | 複雑な制御機構,エラー処理,コルーチンの生成 |
| ゼロおよびプラス: | なし |
| 型の例: | Cont r a |
Continuation モナドの濫用は理解や保守が不可能なコードをつくり出す
可能性があります.
Continuation モナドを使う前に継続渡しスタイル(CPS)について 確実に理解しているか,自身の設計課題について継続が最良のソリューション なのかを確認してください.他の言語では継続が要求されるような多くの アルゴリズムにおいて,Haskell では遅延評価意味論のおかげで,継続を 必要としません.
継続は計算のこの先(future)を表現しており,これは
中間結果から最終結果への関数として表現されます.継続渡しスタイル
では,計算は入れ子になった継続のならびから構築され,最終結果
を生成する最終継続(多くの場合 id)で終了します.
継続は計算のこの先を表現する関数ですから,継続関数を操作することで
複雑な計算のこの先を操作することができます.たとえば,途中での
計算への割込みや,計算の一部の中断,復帰,計算の交互実行などです.
Continutation モナドは CPS をモナドの構造に適合させたものです.
newtype Cont r a = Cont { runCont :: ((a -> r) -> r) } -- r は計算全体の最終の型
instance Monad (Cont r) where
return a = Cont $ \k -> k a -- i.e. return a = \k -> k a
(Cont c) >>= f = Cont $ \k -> c (\a -> runCont (f a) k) -- i.e. c >>= f = \k -> c (\a -> f a k)
|
Continuation モナドは継続渡しスタイルの計算を表現しています.
Cont r a は最終の型が r で,CPS 内部の中間の型
a を生成する CPS 計算です.
return 関数は単純に値を次へ渡すだけの継続を
生成します.>>= 演算子は継続連鎖のなかに束縛された関数を
追加します.
class (Monad m) => MonadCont m where
callCC :: ((a -> m b) -> m a) -> m a
instance MonadCont (Cont r) where
callCC f = Cont $ \k -> runCont (f (\a -> Cont $ \_ -> k a)) k
|
MonadCont クラスは callCC 関数を提供します.
この関数は,Continuation モナド用の脱出継続機構を
提供するものです.脱出継続は現在の計算を中断し,即座に値を返します.
これにより,Error モナドでの throwError および
catchError に類似した効果が得られます.
callCC は現在の継続(Current Continuation)を引数として関数を
呼びます(それゆえ,CallCCという名があります).
callCC を使うときの標準的なイディオムは,λ式を与えて
継続に名前をつけるというものです.そうすると,その名前のついた継続を
スコープ内のあらゆる場所から呼ぶことで,それが入れ子の計算のどんなに
深いところからでも,脱出できます.
callCC によって提供される脱出機構以外にも,Continuation
モナドでは別のより強力な継続の操作を実装できます.しかしながら,
これらの機構は使用法がかなり特殊で,濫用すると簡単コードがわかりにくい
ものになってしまします.それで,ここではこれらをとりあげることは
しません.
この例は脱出継続の機能の感覚を示すものです. 例の関数は,脱出継続を使って,整数上の複雑な変換を実行するものです.
| example18.hs で使えるコード |
|---|
{- Continuation モナドを使って,コードブロックからの「脱出」を行います
この関数は以下のような処理の複雑な制御構造を実装します
Input (n) Output List Shown
========= ====== ==========
0-9 n none
10-199 number of digits in (n/2) digits of (n/2)
200-19999 n digits of (n/2)
20000-1999999 (n/2) backwards none
>= 2000000 sum of digits of (n/2) digits of (n/2)
-}
fun :: Int -> String
fun n = (`runCont` id) $ do
str <- callCC $ \exit1 -> do -- "exit1" を定義
when (n < 10) (exit1 (show n))
let ns = map digitToInt (show (n `div` 2))
n' <- callCC $ \exit2 -> do -- "exit2" を定義
when ((length ns) < 3) (exit2 (length ns))
when ((length ns) < 5) (exit2 n)
when ((length ns) < 7) $ do let ns' = map intToDigit (reverse ns)
exit1 (dropWhile (=='0') ns') --escape 2 levels
return $ sum ns
return $ "(ns = " ++ (show ns) ++ ") " ++ (show n')
return $ "Answer: " ++ str
|
| Prev: Writer モナド | TOC: 目次 | Next: Part III - イントロダクション |