是否可以创建一个函数,以便可以从内到外构造一个Proxy
from管道?由内而外,我的意思是从连接上游和下游连接的函数创建代理。最理想(但不可能)的签名是
makeProxy :: (Monad m) =>
(Server a' a m r -> Client b' b m r -> Effect m r) ->
Proxy a' a b' b m r
我们遇到的第一个问题是构建Proxy
. 我们无法知道函数是否查看Server
或,Client
除非让它们中的每一个都成为M
,在这种情况下,我们只会知道它查看的是哪一个,而不是它试图向上游或下游发送什么值。如果我们专注于上游端,我们唯一知道的就是试图弄清楚上游代理是什么,所以我们需要决定要么总是导致Request
上游更远,要么Respond
ing。无论我们如何回答,我们唯一能提供的价值是()
. 这意味着我们可以Request ()
向上游生产者或Respond ()
立即地。如果我们考虑对两端都进行此选择,则只有四种可能的功能。以下函数以它们的上游和下游连接是向下游 ( D
) 还是上游 ( U
) 发送感兴趣的数据命名。
betweenDD :: (Monad m) =>
(Server () a m r -> Client () b m r -> Effect m r) ->
Proxy () a () b m r
betweenDD = undefined
betweenDU :: (Monad m) =>
(Server () a m r -> Client b' () m r -> Effect m r) ->
Proxy () a b' () m r
betweenDU = undefined
betweenUU :: (Monad m) =>
(Server a' () m r -> Client b' () m r -> Effect m r) ->
Proxy a' () b' () m r
betweenUU f = reflect (betweenDD g)
where g source sink = f (reflect sink) (reflect source)
betweenUD :: (Monad m) =>
(Server a' () m r -> Client () b m r -> Effect m r) ->
Proxy a' () () b m r
betweenUD = undefined
betweenDD
最有趣的是,它会在 aProducer
和 a之间建立一个管道Consumer
;betweenUU
将对上游运行的管道执行相同的操作。betweenDU
将消耗从两个来源之一请求它的数据。betweenUD
将产生数据,将其发送到两个目的地之一。
我们可以提供一个定义betweenDD
吗?如果没有,我们能否改为为以下更简单的函数提供定义?
belowD :: (Monad m) =>
(Producer a m r -> Producer b m r) ->
Proxy () a () b m r
aboveD :: (Monad m) =>
(Consumer b m r -> Consumer a m r) ->
Proxy () a () b m r
这个问题的动机是试图写belowD
来回答关于P.zipWith
.
例子
这个例子恰好是激发这个问题的问题。.
假设我们要创建一个Pipe
将number
通过它的值。将Pipe
具有a
从上方传到下游的值和从(n, a)
下方离开下游的值;换句话说,它将是一个Pipe a (n, a)
.
我们将通过zip
ping 数字来解决这个问题。zip
用数字 ing的结果是一个(->)
从 aProducer a
到 a的函数Producer (n, a)
。
import qualified Pipes.Prelude as P
number' :: (Monad m, Num n, Enum n) => Producer a m () -> Producer (n, a) m ()
number' = P.zip (fromList [1..])
即使Pipe
会从上游消耗a
s,但从函数的角度来看,它需要 a Producer
of a
s 来提供这些值。如果我们有一个定义,belowD
我们可以写
number :: (Monad m, Num n, Enum n) => Pipe a (n, a) m ()
number = belowD (P.zip (fromList [1..]))
给定一个合适的定义fromList
fromList :: (Monad m) => [a] -> Producer a m ()
fromList [] = return ()
fromList (x:xs) = do
yield x
fromList xs