m
パラメータを使用してPipe a b m r
を接続すると、パイプが動作しているMonad
がスワップアウトされます。これを使用して、パイプの下流側をキューにリンクを張っている別のパイプに接続し、パイプの上流側をキューからリンクを読み取るパイプに接続することで、リンクを再キューに入れることができます。
私たちの目標は、私たちは、その下流の出力、Either l b
、下流送信するために上流返信するLeft l
かのいずれかであるパイプを取り、後ろにl
Sをお送りします
import Pipes
loopLeft :: Monad m => Pipe (Either l a) (Either l b) m r -> Pipe a b m r
を書くことですアップストリーム入力Either l a
は、アップストリームから来るLeft l
またはRight a
のキューに入っています。 Left l
を一緒に接続して、上流から来るのはa
しか見えないパイプを作り、下流にはb
があります。
下流側でLeft l
からl
をスタックにプッシュします。我々は、yield
r
をRight r
下流から得た。
import Control.Monad
import Control.Monad.Trans.State
pushLeft :: Monad m => Pipe (Either l a) a (StateT [l] m) r
pushLeft = forever $ do
o <- await
case o of
Right a -> yield a
Left l -> do
stack <- lift get
lift $ put (l : stack)
上流の端では、スタックの上の何かをyield
に探します。存在しない場合は、上流からの値はawait
、それにはyield
です。
popLeft :: Monad m => Pipe a (Either l a) (StateT [l] m) r
popLeft = forever $ do
stack <- lift get
case stack of
[] -> await >>= yield . Right
(x : xs) -> do
lift $ put xs
yield (Left x)
今、私たちは、loopLeft
を書くことができます。私たちはパイプ構成popLeft >-> hoist lift p >-> pushLeft
と一緒にアップストリームとダウンストリームのパイプを構成します。 hoist lift
はPipe a b m r
をPipe a b (t m) r
に変えます。 distribute
は、Pipe a b (t m) r
をt (Pipe a b m) r
に変えます。 Pipe a b m r
に戻るには、空のスタック[]
で始まるStateT
の計算全体を実行します。 Pipes.Lift
には、evalStateT
とdistribute
の組み合わせのためのいい名前evalStateP
があります。
import Pipes.Lift
loopLeft :: Monad m => Pipe (Either l a) (Either l b) m r -> Pipe a b m r
loopLeft p = flip evalStateT [] . distribute $ popLeft >-> hoist lift p >-> pushLeft
私はおそらく 'process ::(MonadState CState m、MonadIO m)=>パイプ項目項目m()'を持っています。あまりコードが変更されず(おそらく)、読みやすくなり、モナドスタックの実装の詳細が抽象化されます。 – Alec