2016-12-01 65 views
6

默认管道是基于拉的。这是由于运营商>->,这是通过+>>实现的,这是bind运营商针对他的拉动类别。我的理解是,这意味着如果你有代码producer >-> consumer,消费者的身体将被首先调用,然后一旦它等待数据,生产者将被调用。如何将基于拉的管道变为基于推拉的管道?

我见过的pipes文档here,你可以使用代码(reflect .)Pipes.Core把一个基于拉管道进入基于推送管英寸这意味着相反(如果我错了,请纠正我)在producer >-> consumer以上的代码中,生产者首先运行,生成一个值,然后消费者尝试消费。这似乎很有用,我想知道如何去做。

我也看到讨论here没有基于推送对口>->因为它很容易把任何管道周围(我假设有反映?),但我真的不能弄清楚如何做到这一点或找到任何例子。

下面是一些代码,我已经尝试:

stdin :: Producer String IO r 
stdin = forever $ do 
    lift $ putStrLn "stdin" 
    str <- lift getLine 
    yield str 

countLetters :: Consumer String IO r 
countLetters = forever $ do 
    lift $ putStrLn "countLetters" 
    str <- await 
    lift . putStrLn . show . length $ str 

-- this works in pull mode 
runEffect (stdin >-> countLetters) 

-- equivalent to above, works 
runEffect ((\() -> stdin) +>> countLetters) 

-- push based operator, doesn't do what I hoped 
runEffect (stdin >>~ (\_ -> countLetters)) 

-- does not compile 
runEffect (countLetters >>~ (\() -> stdin)) 

回答

2
-- push based operator, doesn't do what I hoped 
runEffect (stdin >>~ (\_ -> countLetters)) 

我在这里集会的问题是,当制片人跑第一不出所料,第一个生产值下降。 ...比较

GHCi> runEffect (stdin >-> countLetters) 
countLetters 
stdin 
foo 
3 
countLetters 
stdin 
glub 
4 
countLetters 
stdin 

...有:

GHCi> runEffect (stdin >>~ (\_ -> countLetters)) 
stdin 
foo 
countLetters 
stdin 
glub 
4 
countLetters 
stdin 

这个问题进行了详细加布里埃尔冈萨雷斯回答this question讨论。这归结于您给(>>~)的函数的参数是基于推送的流程中的“驱动”输入,所以如果您const它离开,您最终将丢弃第一个输入。解决的办法是重塑countLetters相应:

countLettersPush :: String -> Consumer String IO r 
countLettersPush str = do 
    lift $ putStrLn "countLetters" 
    lift . putStrLn . show . length $ str 
    str' <- await 
    countLettersPush str' 
GHCi> runEffect (stdin >>~ countLettersPush) 
stdin 
foo 
countLetters 
3 
stdin 
glub 
countLetters 
4 
stdin 

我也看到讨论here没有基于推送对口>->因为它很容易把周围的任何管道(我假设与反映?)

我不完全确定我的理由,但它似乎并不完全适用于上述解决方案。我们可以做的,现在我们有基于推送的流量正常工作,使用reflect把它绕回拉式流程:

-- Preliminary step: switching to '(>~>)'. 
stdin >>~ countLettersPush 
(const stdin >~> countLettersPush)() 

-- Applying 'reflect', as the documentation suggests. 
reflect . (const stdin >~> countLettersPush) 
reflect . const stdin <+< reflect . countLettersPush 
const (reflect stdin) <+< reflect . countLettersPush 

-- Rewriting in terms of '(+>>)'. 
(reflect . countLettersPush >+> const (reflect stdin))() 
reflect . countLettersPush +>> reflect stdin 

这的确是拉式,作为流由reflect stdin驱动,下游Client

GHCi> :t reflect stdin 
reflect stdin :: Proxy String()() X IO r 
GHCi> :t reflect stdin :: Client String() IO r 
reflect stdin :: Client String() IO r :: Client String() IO r 

的流动,但是,包括发送String s上行,所以它不能在(>->),这是来表示,所以,仅下游地说,:

GHCi> -- Compare the type of the second argument with that of 'reflect stdin' 
GHCi> :t (>->) 
(>->) 
    :: Monad m => 
    Proxy a' a() b m r -> Proxy() b c' c m r -> Proxy a' a c' c m