あなたが探している用語はフリーモナドトランスフォーマーです。これらがどのように機能するかを学ぶには、The Monad Reader の第 19 号の「コルーチン パイプライン」の記事を読むのが最適です。Mario Blazevic は、このタイプがどのように機能するかを非常に明快に説明していますが、彼はそれを「コルーチン」タイプと呼んでいます。
彼のタイプをパッケージに書き留めた後、新しい公式ホームであるパッケージにtransformers-free
マージされました。free
あなたのCallback
型は以下と同型です:
type Callback a = forall r . FreeT ((->) a) IO r
フリー モナド トランスフォーマーを理解するには、まずフリー モナド を理解する必要があります。フリー モナドは単なる抽象構文木です。free モナドに、構文ツリーの単一のステップを定義するファンクターを与えると、基本的にそれらのタイプのステップのリストであるMonad
from が作成されます。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
自動的にMonad
s でf
あり、この場合((->) a)
は functor であるため、自動的に正しいことを行います。それが圏論の魔法です!
MonadTrans
また、 を使用するためにインスタンスを定義する必要はありませんでしたlift
。 FreeT f
functor が与えられると、自動的にモナド変換子に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
printer
using を定義する方法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
をお勧めします。