Скажем, у меня простая модель производителя/потребителя, где потребитель хочет передать некоторое состояние производителю. Например, пусть объекты, идущие вниз по потоку, являются объектами, которые мы хотим записать в файл, а восходящие объекты - это некоторый токен, представляющий, где объект был записан в файле (например, смещение).
Эти два процесса могут выглядеть примерно так (с 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] ]
Простым, как это могло бы быть, у меня была небольшая трудность в рассуждениях о том, как их составлять. В идеале нам нужен потоковый контроль управления, как показано ниже,
-
writeObjects
начинается с блокировки наrequest
, отправив исходныйObjId 0
вверх по течению. -
produceObjects
отправляет первый объектObj 0
, downstream -
writeObjects
записывает объект и увеличивает его состояние и ждетrequest
, на этот раз отправивObjId 1
upstream -
respond
вproduceObjects
возвращается сObjId 0
-
produceObjects
продолжается на этапе (2) со вторым объектом,Obj 1
Моя первоначальная попытка заключалась в том, что композиция на основе нажатия следующая,
main = void $ run $ produceObjects objects >>~ const writeObjects
Обратите внимание на использование const
для работы с другими несовместимыми типами (это, вероятно, проблема). В этом случае, однако, мы обнаруживаем, что ObjId 0
съедается,
Producing Obj 0
Wrote Obj 0
Object Obj 0 has ID ObjId 1
Producing Obj 1
...
Потянутый подход,
main = void $ run $ const (produceObjects objects) +>> writeObjects
имеет аналогичную проблему, на этот раз снижая Obj 0
.
Как можно сочинять эти фрагменты желаемым образом?