あなたが探している用語はフリーモナドトランスフォーマーです。これらがどのように機能するかを学ぶには、The Monad Reader の第 19 号の「コルーチン パイプライン」の記事を読むのが最適です。Mario Blazevic は、このタイプがどのように機能するかを非常に明快に説明していますが、彼はそれを「コルーチン」タイプと呼んでいます。
彼のタイプをパッケージに書き留めた後、新しい公式ホームであるパッケージにtransformers-freeマージされました。free
あなたのCallback型は以下と同型です:
type Callback a = forall r . FreeT ((->) a) IO r
フリー モナド トランスフォーマーを理解するには、まずフリー モナド を理解する必要があります。フリー モナドは単なる抽象構文木です。free モナドに、構文ツリーの単一のステップを定義するファンクターを与えると、基本的にそれらのタイプのステップのリストであるMonadfrom が作成されます。Functorあなたが持っていた場合:
Free ((->) a) r
これは、ゼロ個以上aの s を入力として受け取り、 value を返す構文ツリーになりますr。
ただし、通常、効果を埋め込むか、構文ツリーの次のステップを何らかの効果に依存させたいと考えています。これを行うには、単純に自由モナドを自由モナド変換子に昇格させます。これは、構文ツリーのステップ間でベース モナドをインターリーブします。あなたのCallbackタイプの場合、各入力ステップの間にインターリーブIOしているので、ベースモナドはIO次のとおりです。
FreeT ((->) a) IO r
フリーモナドの良いところは、それらが自動的に任意のファンクターのモナドになることです。そのため、これを利用してdo記法を使用して構文ツリーを組み立てることができます。たとえばawait、モナド内で入力をバインドするコマンドを定義できます。
import Control.Monad.Trans.Free
await :: (Monad m) => FreeT ((->) a) m a
await = liftF id
Callbackこれで、 sを書くための DSL ができました。
import Control.Monad
import Control.Monad.Trans.Free
printer :: (Show a) => FreeT ((->) a) IO r
printer = forever $ do
a <- await
lift $ print a
Monad必要なインスタンスを定義する必要がないことに注意してください。FreeT fとはどちらも functor に対してFree f自動的にMonads でfあり、この場合((->) a)は functor であるため、自動的に正しいことを行います。それが圏論の魔法です!
MonadTransまた、 を使用するためにインスタンスを定義する必要はありませんでしたlift。 FreeT ffunctor が与えられると、自動的にモナド変換子にfなるので、それも処理してくれます。
私たちのプリンターは適しCallbackた です。
feed :: [a] -> FreeT ((->) a) IO r -> IO ()
feed as callback = do
x <- runFreeT callback
case x of
Pure _ -> return ()
Free k -> case as of
[] -> return ()
b:bs -> feed bs (k b)
をバインドすると実際の出力が行わrunFreeT callbackれ、リストの次の要素をフィードする構文ツリーの次のステップが得られます。
試してみよう:
>>> feed [1..5] printer
1
2
3
4
5
ただし、これらすべてを自分で書く必要さえありません。Petr が指摘したように、私のpipesライブラリは、このような一般的なストリーミング パターンを抽象化します。コールバックは次のとおりです。
forall r . Consumer a IO r
printerusing を定義する方法pipesは次のとおりです。
printer = forever $ do
a <- await
lift $ print a
...そして、次のように値のリストをフィードできます。
>>> runEffect $ each [1..5] >-> printer
1
2
3
4
5
pipes私は、これらのような非常に広範囲のストリーミング抽象化を包含するように設計し、do各ストリーミング コンポーネントを構築するために常に表記法を使用できるようにしました。 pipesには、状態やエラーの処理、情報の双方向フローなどのさまざまな洗練されたソリューションも付属しているため、 のCallback観点から抽象化を定式化するpipesと、大量の便利な機構を無料で利用できます。
について詳しく知りたい場合は、チュートリアルを読むことpipesをお勧めします。