Sie können einen Pipe a b m r
an einen Nebeneffekt über den Parameter m
anschließen, der austauscht, welche Monad
die Leitung über arbeitet. Sie können dies verwenden, um Verbindungen erneut zu aktivieren, indem Sie das Downstream-Ende Ihrer Pipe mit einer anderen Pipe verbinden, die die Links in einer Warteschlange festhält und das Upstream-Ende Ihrer Pipe mit einer Pipe verbindet, die Links aus der Warteschlange liest.
Unser Ziel ist es
import Pipes
loopLeft :: Monad m => Pipe (Either l a) (Either l b) m r -> Pipe a b m r
schreiben Wir nehmen ein Rohr, dessen Downstream-Ausgang, Either l b
, ist entweder ein Left l
zurück stromaufwärts zu senden oder eine Right b
stromabwärts, zu senden und die l
senden s zurück in der stromaufwärtige Eingang Either l a
, die entweder ein oder ein Left l
Right a
aus Richtung stromauf Warteschlange gestellt. Wir verbinden die Left l
s zusammen ein Rohr zu machen, die a
s von Upstream nur kommen sieht und liefert nur b
s im Downstream geleitet.
Am unteren Ende werden wir die l
s von Left l
auf einen Stapel schieben. Wir yield
die r
s von Right r
Downstream.
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)
am oberen Ende etwas zu yield
oben auf dem Stapel sehen wir. Wenn es keinen gibt, werden wir await
für einen Wert von stromaufwärts und yield
es.
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)
Jetzt können wir loopLeft
schreiben. Wir komponieren die vor- und nachgelagerten Rohre zusammen mit der Rohrzusammensetzung popLeft >-> hoist lift p >-> pushLeft
. Die hoist lift
wandelt eine Pipe a b m r
in eine Pipe a b (t m) r
um. Die distribute
wandelt eine Pipe a b (t m) r
in eine t (Pipe a b m) r
um. Um zu einem Pipe a b m r
zurück zu kommen, führen wir die gesamte StateT
Berechnung beginnend mit einem leeren Stapel []
aus. In Pipes.Lift
gibt es einen schönen Namen evalStateP
für die Kombination von evalStateT
und distribute
.
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
Egal, aber ich hätte wahrscheinlich 'Prozess :: (MonadState CState m, MonadIO m) => Pipe Item Item m()'.Nicht viel Code ändert (wahrscheinlich), lesbarer und abstrahiert die Implementierungsdetails Ihres Monadenstapels. – Alec