Ответ 1
Возможно, вам захочется использовать пакет сетевой кабель. Он описывает сетевое приложение как то, что дается двум "конечным точкам" - один приемник передает данные в сокет и один источник, который считывает данные из сокета:
type Application m = AppData m -> m ()
data AppData m Source -- ...
appSource :: AppData m -> Source m ByteStringSource
appSink :: AppData m -> Sink ByteString m ()
Это чисто отделяет запись и часть чтения. Теперь вы можете делать все, что захотите, с таким источником и раковиной, даже передавая каждый другой поток и обрабатывая вход и выход отдельно. Конечно, каждый из них может читать или писать только в зависимости от конечной точки, которую вы ему даете.
Если вы хотите обеспечить однопоточную обработку, вы можете ограничить себя реализацией своих программных компонентов как Conduit ByteString m ByteString
. Такой трубопровод можно с радостью превратить в Application
как
asApp :: MonadIO m => Conduit ByteString m ByteString -> Application m
asApp cond ad = appSource ad $= cond $$ appSink ad
Но канал может запрашивать данные только с помощью await
и записывать выходные данные с помощью yield
, иначе не имеет доступа к каким-либо ручкам и никогда не видит ни одной из его конечных точек, поэтому он не может выставлять или удалять их в любом месте.