明らかにMonadCont
s はより制限されておりMonad
、そのおかげでプレーンな sよりも強力callCC
です。これは、そのインスタンスが少なくなり、より多くのことができることを意味します。
の定義されたインスタンスを見るとMonadCont
、そこにリストされているすべてのものが 、または既存のインスタンスのいずれCont
かを必要とするように見えます。つまり、いくつかの or から開始する必要があり、特に に変えることはできません。ContT
MonadCont
Cont
ContT
IO
MonadCont
ただし、コンテキストで使用するのは理にかなっていると思うので、以下を簡略化できます (公式の Hackage ページのcallCC
例から調整):IO
callCC
whatsYourName :: IO ()
whatsYourName = do
name <- getLine
let response = flip runCont id $ do
callCC $ \exit -> do
when (null name) (exit "You forgot to tell me your name!")
return $ "Welcome, " ++ name ++ "!"
print response
の中へ
whatsYourName' :: IO ()
whatsYourName' = do
name <- getLine
response <- callCC $ \exit -> do
when (null name) (exit "You forgot to tell me your name!")
return $ "Welcome, " ++ name ++ "!"
print response
callCC
よりクリーンな方法で do ブロックで使用します。
当然のことながら、IO
のインスタンスを作成するにMonadCont
は何らかの魔法が必要です。なぜなら、callCC
forIO
は「未来の計算で指定された関数を呼び出して、現実の世界で次に何が起こるかを指定する」ため、実際にこれが何を意味するかを知ることができるのはインタープリターまたはコンパイラーだけです。 . 一方、これがインポート可能であるという理論的な理由はわかりませんでした。これは、Scheme が既に長い間それを持っており、そのようなインスタンスを作成するために言語を変更する必要がまったくないためです。
考えられる問題
私が考えることができる 1 つの要因は、 is のセマンティクスがcallCC
適切なクリーンアップの保証と競合することです。多くの言語は、適切なクリーンアップのために「try...finally」制御を提供しており、C++ のデストラクタもそれを保証しています。Haskell でそれが何であるかはわかりませんが、callCC
利用できる場合はIO
、それを使用して、クリーンアップが必要な関連するコンテキストから逃れることができます。そのため、Ruby で何が起こるかIO
を確認できるように、sush 保証を提供することは不可能になります。
意見の議論
@jozefg からの回答は非常に優れています。ここに私の意見を書き留めておきたいと思います。
MonadCont
mtlから来ているのは本当です。しかし、それは、正しい定義がコンパイルモジュールのスコープ内にあり、設定されているunsafeCallCC
場合、GHCまたは他のコンパイラがインスタンスを定義および定義できないという意味ではありません。MonadCont
-XIOMonadCont
例外の安全性については既に説明しましたが、それについて確信を持つのは難しいようです。しかし、私の意見では、Haskell には既に があり
unsafePerformIO
、これは基本的に よりも安全ではありません。unsafeCallCC
callCC
ほとんどの場合、原因は強力すぎるため、可能な限り避ける必要があります。ただし、私の意見では、継続渡しスタイルを使用して遅延評価を明示的にすることができます。これにより、プログラムをよりよく理解し、最適化の可能性を見つけやすくなります。もちろんCPS は ではありませんMonadCont
が、それを使用して深くネストされた内部関数を do 表記に変換するのは自然なステップです。