コードに愚かなエラーがあります。これが正しい実装です
newtype CFix m = CFix { goOn :: CFix m -> m (CFix m) }
yinyang :: (MonadIO m, MonadCont m) => m (CFix m)
yinyang = do
yin <- (\c -> liftIO (putStr "@") >> return c) =<< (callCC $ \k -> return (CFix k))
yang <- (\c -> liftIO (putStr "*") >> return c) =<< (callCC $ \k -> return (CFix k))
goOn yin yang
これを実行するのはとても簡単です。
main :: IO ()
main = runContT yinyang $ void.return
あるいは
main :: IO ()
main = runContT yinyang undefined
後は怖そうですが、継続は評価される機会がないので安心です。(全体の式は、_|_止まらないので値に評価されます)
期待される結果を出力します
@*@**@***...
説明した
元の試みは、Scheme バージョンを直接翻訳することです。
(let* (
(yin
((lambda (cc) (display #\@) cc) (call-with-current-continuation (lambda (c) c))))
(yang
((lambda (cc) (display #\*) cc) (call-with-current-continuation (lambda (c) c)))))
(yin yang))
ハスケルに。型付き言語の場合、上記の型チェックを行うための鍵は、 にt同型の型を持つことt -> tです。Haskell では、newtypeキーワードを使用してこれを行います。また、副作用を持たせるには が必要ですIOが、サポートしていませんcallCC。後でサポートするには、 が必要MonadContです。そのため、 と の両方を扱うには が必要MonadIOですMonadCont。また、 はそれが何に取り組んでいるのnewtypeかを知る必要があるため、型パラメーターとして を運ぶ必要があります。だから今、私たちは書きますMonadMonad
newtype CFix m = ...
yinyang :: (MonadIO m, MonadCont m) => m (CFix m)
で作業しているので、表記Monadを使用すると便利です。doしたがって、let*代入は および に変換されyin <-ますyang <-。MonadIOtoではdisplayを使用しますliftIO.putStr。にcall-with-current-continuation翻訳しcallCCますが、明らかに翻訳することはできませんid。これはしばらく置いておきます。
私の間違いは、表示ブロックとブロックの組み合わせ演算子を単純に に変換するcallCCこと>>=です。Scheme やその他の厳密な言語では、パラメーターは式の前に評価されるため、callCCブロックは表示ブロックの前に実行されます。その結果、=<<代わりにを使用し>>=ます。コードは次のようになりました
newtype CFix m = ...
yinyang :: (MonadIO m, MonadCont m) => m (CFix m)
yinyang = do
yin <- (\c -> liftIO (putStr "@") >> return c) =<< (callCC $ ...)
yang <- (\c -> liftIO (putStr "*") >> return c) =<< (callCC $ ...)
...
ここで、型チェックを行い、s に何を入れるかを確認します...。callCCさんのサインは
MonadCont m => ((a -> m b) -> m a) -> m a
そのパラメータは型を持っています
MonadCont m => (a -> m b) -> m a
いくつかのタイプaとb. これまでに記述されたコードを見ると、と が同じ型のs return valueを持つyinと簡単に結論付けられます。ただし、元のスキーマ バージョンではandを関数として使用しているため、 type があります。したがって、ここで再帰型と が必要になります。yangcallCCm ayinyangp -> rnewtype
単純な方法を取得するm aには usereturnが必要であり、 type を持つものが必要ですa。これがこれから定義しようとしている型コンストラクターから来ているとしましょう。パラメータを提供するには、 fromcallCCを構築する必要があります。したがって、コンストラクタは次のようになります。しかし、何ですか?簡単な選択は、同じ を使用することです。したがって、次の定義があります。a(a -> m b)baCFix
newtype CFix m = CFix { goOn :: CFix m -> m (CFix m) }
およびcallCCのパラメータの実装
\k -> return (CFix k)
コンストラクターを使用して指定されたパラメーターからCFixを構築し、 を使用して目的の型にラップします。CFixreturn
yinでは、 (型のm (CFix m)) を関数としてどのように使用するのでしょうか? 型デストラクタgoOnを使用すると、内部関数を抽出できるため、 last の定義があり...ます。
newtype CFix m = CFix { goOn :: CFix m -> m (CFix m) }
yinyang :: (MonadIO m, MonadCont m) => m (CFix m)
yinyang = do
yin <- (\c -> liftIO (putStr "@") >> return c) =<< (callCC $ \k -> return (CFix k))
yang <- (\c -> liftIO (putStr "*") >> return c) =<< (callCC $ \k -> return (CFix k))
goOn yin yang
これはプログラムの最終バージョンです。