如何将基于拉力的管道转变为基于推力的管道?

时间:2016-12-01 21:15:03

标签: haskell haskell-pipes

默认情况下,管道是基于拉取的。这是由于>->运算符由+>>实现的,bind是其拉取类别的有点producer >-> consumer运算符。我的理解是,这意味着如果你有像pipes这样的代码,将首先调用消费者的身体,然后一旦等待数据,就会调用生产者。

我在(reflect .)文档here中看到,您可以使用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)) 没有基于推送的对应物,因为很容易转换任何管道(我假设有反射?),但我不能真的想知道如何做或找到任何例子。

以下是我尝试的一些代码:

setMouseTracking(true);

1 个答案:

答案 0 :(得分: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

... with:

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

Gabriel Gonzalez&#39;详细讨论了这个问题。回答this question。它归结为你给(>>~)的函数的参数是如何驱动&#34;驾驶&#34;在基于推送的流程中输入,所以如果你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,因此无法用(>->)来表示,也就是说,仅限于下游:

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