2

我正在使用管道 4.0.0。在该库中,管道向下游产生的产量数量通常与来自上游的等待数量无关。

但是假设我想构建一个受限管道,强制为每个等待执行一个且只有一个 yield,同时仍然能够使用 monadic (>>=) 对这些类型的管道进行排序。

我观察到,在双向情况下,代理从上游请求的每个值都与发回的值匹配。所以也许我正在寻找的是一个类型函数,Proxy a' a () b m r -> Pipe a (Either b a') m r它“反映”上游的值,将它们转化为下游的额外收益。或者,不那么普遍,Client a' a -> Pipe a a'. 这样的功能可能吗?

4

2 回答 2

4

你绝对不想用pipes这个。但是,您可以做的是定义一个受限类型来执行此操作,在该受限类型中执行所有连接和逻辑,然后在完成后将其提升为 a Pipe

您想要的类型是这样的,类似于netwire Wire

{-# LANGUAGE DeriveFunctor #-}

import Control.Monad.Trans.Free  -- from the 'free' package

data WireF a b x = Pass (a -> (b, x)) deriving (Functor)

type Wire a b = FreeT (WireF a b)

这自动是一个 monad 和一个 monad 转换器,因为它是根据FreeT. 然后就可以实现这个方便的操作了:

pass :: (Monad m) => (a -> b) -> Wire a b m ()
pass f = liftF $ Pass (\a -> (f a, ()))

...并使用单子语法组装自定义电线:

example :: Wire Int Int IO ()
example = do
    pass (+ 1)
    lift $ putStrLn "Hi!"
    pass (* 2)

然后,当您完成使用此受限Wire类型连接事物时,您可以将其提升为Pipe

promote :: (Monad m) => Wire a b m r -> Pipe a b m r
promote w = do
    x <- lift $ runFreeT w
    case x of
        Pure r -> return r
        Free (Pass f) -> do
            a <- await
            let (b, w') = f a
            yield b
            promote w'

请注意,您可以定义标识以及线和线组合:

idWire :: (Monad m) => Wire a a m r
idWire = forever $ pass id

(>+>) :: (Monad m) => Wire a b m r -> Wire b c m r -> Wire a c m r
w1 >+> w2 = FreeT $ do
    x <- runFreeT w2
    case x of
        Pure       r   -> return (Pure r)
        Free (Pass f2) -> do
            y <- runFreeT w1
            case y of
                Pure       r   -> return (Pure r)
                Free (Pass f1) -> return $ Free $ Pass $ \a ->
                        let (b, w1') = f1 a
                            (c, w2') = f2 b
                        in  (c, w1' >+> w2')

我很确定这些形成Category

idWire >+> w = w

w >+> idWire = w

(w1 >+> w2) >+> w3 = w1 >+> (w2 >+> w3)

另外,我很确定它promote遵守以下函子定律:

promote idWire = cat

promote (w1 >+> w2) = promote w1 >-> promote w2
于 2013-09-22T20:03:52.280 回答
0

我的直觉是,这将很难做到,如果不是完全不可能的话。你不仅可以编写充满复杂循环的生产者和消费者,而且一元接口还可以让消费者的控制流依赖于它从生产者那里获得的值。

consumer = do
  n <- await
  for i in 1..n do
     m <- await
     print m

很难对生产者的类型进行编码,即“这会产生 N + 1 个数字,其中 N 是产生的第一个数字的值”。


回到主题,我认为如果您使用自己的组合器而不是管道的基本单子接口,您可能会有更好的机会。例如,boomerang web 路由库使用一组组合器来同时构建进行 (Route -> URL) 转换的代码和进行 (URL -> Route) 转换的代码,从而保证它们是兼容和逆向的彼此的。

于 2013-09-22T17:54:20.560 回答