Powiedzmy, że mam prosty model producenta/konsumenta, w którym konsument chce przekazać producentowi pewne państwo. Na przykład, niech obiekty przepływające dalej będą obiektami, które chcemy zapisać do pliku, a obiektami upstream będzie jakiś token reprezentujący miejsce, w którym obiekt został zapisany w pliku (na przykład przesunięcie).Idiomatyczne lampy dwukierunkowe z dalszym stanem bez utraty
Te dwa procesy może wyglądać następująco (z pipes-4.0
)
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
import Pipes
import Pipes.Core
import Control.Monad.Trans.State
import Control.Monad
newtype Object = Obj Int
deriving (Show)
newtype ObjectId = ObjId Int
deriving (Show, Num)
writeObjects :: Proxy ObjectId Object() X IO r
writeObjects = evalStateT (forever go) (ObjId 0)
where go = do i <- get
obj <- lift $ request i
lift $ lift $ putStrLn $ "Wrote "++show obj
modify (+1)
produceObjects :: [Object] -> Proxy X() ObjectId Object IO()
produceObjects = go
where go [] = return()
go (obj:rest) = do
lift $ putStrLn $ "Producing "++show obj
objId <- respond obj
lift $ putStrLn $ "Object "++show obj++" has ID "++show objId
go rest
objects = [ Obj i | i <- [0..10] ]
Proste jak to może być, miałem uczciwej trochę trudności rozumowanie o tym, jak je skomponować. Idealnie, którą chcemy przepływ push-oparty kontroli jak na poniższej ilustracji,
writeObjects
rozpoczyna się od blokowania narequest
, wysławszy początkowąObjId 0
upstream.produceObjects
wysyła pierwszy obiekt,Obj 0
, dalszegowriteObjects
pisze przedmiotu i zwiększa swój stan, i czeka narequest
, tym razem wysyłającObjId 1
upstreamrespond
wproduceObjects
zwrotów zObjId 0
produceObjects
kontynuuje w kroku (2) z drugim obiektem,Obj 1
Moja pierwsza próba to ze składu push-oparty następująco,
main = void $ run $ produceObjects objects >>~ const writeObjects
Uwaga wykorzystanie const
aby obejść inaczej niezgodnych typów (jest to prawdopodobne, gdzie leży problem). W tym przypadku jednak, okazuje się, że ObjId 0
zostanie zjedzone,
Producing Obj 0
Wrote Obj 0
Object Obj 0 has ID ObjId 1
Producing Obj 1
...
Podejście oparte pociągowy,
main = void $ run $ const (produceObjects objects) +>> writeObjects
cierpi podobny problem, tym razem spada Obj 0
.
Jak można skomponować te elementy w pożądany sposób?