Giả sử tôi có mô hình sản xuất/tiêu dùng đơn giản, nơi người tiêu dùng muốn trả lại một số trạng thái cho nhà sản xuất. Ví dụ, để cho các đối tượng lưu lượng hạ lưu là các đối tượng mà chúng ta muốn ghi vào một tệp và các đối tượng ngược dòng là một số mã đại diện cho nơi đối tượng được ghi trong tệp (ví dụ: một offset).Ống hai chiều thành ngữ với trạng thái hạ lưu mà không bị mất
Hai quá trình có thể giống như thế này (với 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] ]
Đơn giản vì điều này có thể được, tôi đã có một chút công bằng của khó khăn lý luận về cách soạn chúng. Lý tưởng nhất, chúng tôi muốn một dòng chảy đẩy dựa trên tầm kiểm soát như sau,
writeObjects
bắt đầu bằng cách chặn trênrequest
, sau khi gửi ban đầuObjId 0
thượng nguồn.produceObjects
gửi đối tượng đầu tiên,Obj 0
, hạ lưuwriteObjects
viết đối tượng và increments trạng thái của nó, và chờ đợi trênrequest
, lần này gửiObjId 1
thượng nguồnrespond
trongproduceObjects
lợi nhuận vớiObjId 0
produceObjects
tiếp tục ở Bước (2) với đối tượng thứ hai,Obj 1
Nỗ lực ban đầu của tôi là với thành phần push-based như sau,
main = void $ run $ produceObjects objects >>~ const writeObjects
Lưu ý việc sử dụng const
để làm việc xung quanh các loại khác không tương thích (đây là khả năng mà vấn đề nằm). Trong trường hợp này, tuy nhiên, chúng tôi thấy rằng ObjId 0
được ăn,
Producing Obj 0
Wrote Obj 0
Object Obj 0 has ID ObjId 1
Producing Obj 1
...
Một cách tiếp cận theo mô hình pull,
main = void $ run $ const (produceObjects objects) +>> writeObjects
bị một vấn đề tương tự, lần này thả Obj 0
.
Làm thế nào người ta có thể sắp xếp các phần này theo cách mong muốn?