6

默认情况下,管道是基于拉的。这是由于实现的操作>->符是他的拉动类别+>>的重要操作符。bind我的理解是,这意味着如果你有类似producer >-> consumer的代码,首先会调用消费者的主体,然后一旦等待数据,就会调用生产者。

我在这里pipes的文档中看到,您可以使用代码将基于拉的管道转换为基于推的管道。这意味着相反(如果我错了,请纠正我)在上面的代码中,生产者首先运行,产生一个值,然后消费者尝试消费。这似乎真的很有用,我想知道该怎么做。(reflect .)Pipes.Coreproducer >-> consumer

我在这里的讨论中也看到,没有基于推送的对应物,>->因为它很容易扭转任何管道(我假设使用反射?),但我真的不知道如何去做或找到任何例子。

这是我尝试过的一些代码:

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))
4

1 回答 1

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

Gabriella Gonzalez 对这个问题的回答详细讨论了这个问题。它归结为您提供给函数的参数如何(>>~)是基于推送的流程中的“驱动”输入,因此,如果您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

我在这里的讨论中也看到没有基于推送的对应物,>->因为它很容易转动任何管道(我假设使用反射?)

我不完全确定自己的立场,但似乎并不完全适用于上述解决方案。我们可以做的是,现在我们已经使基于推送的流程正常工作,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 
于 2016-12-03T05:25:58.993 回答