コードに愚かなエラーがあります。これが正しい実装です
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
かを知る必要があるため、型パラメーターとして を運ぶ必要があります。だから今、私たちは書きますMonad
Monad
newtype CFix m = ...
yinyang :: (MonadIO m, MonadCont m) => m (CFix m)
で作業しているので、表記Monad
を使用すると便利です。do
したがって、let*
代入は および に変換されyin <-
ますyang <-
。MonadIO
toでは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 があります。したがって、ここで再帰型と が必要になります。yang
callCC
m a
yin
yang
p -> r
newtype
単純な方法を取得するm a
には usereturn
が必要であり、 type を持つものが必要ですa
。これがこれから定義しようとしている型コンストラクターから来ているとしましょう。パラメータを提供するには、 fromcallCC
を構築する必要があります。したがって、コンストラクタは次のようになります。しかし、何ですか?簡単な選択は、同じ を使用することです。したがって、次の定義があります。a
(a -> m b)
b
a
CFix
newtype CFix m = CFix { goOn :: CFix m -> m (CFix m) }
およびcallCC
のパラメータの実装
\k -> return (CFix k)
コンストラクターを使用して指定されたパラメーターからCFix
を構築し、 を使用して目的の型にラップします。CFix
return
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
これはプログラムの最終バージョンです。