gpt4 book ai didi

haskell - 如何将基于拉的管道变成基于推的管道?

转载 作者:行者123 更新时间:2023-12-04 15:40:54 24 4
gpt4 key购买 nike

默认情况下,管道是基于拉的。这是由于运营商 >->通过 +>> 实现这是有意义的bind运算符(operator)为他的拉类别。我的理解是,这意味着如果你有像 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))

最佳答案

-- 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's answer 详细讨论了这个问题至 this question .它归结为你给 (>>~) 的函数的参数如何。是基于推送的流程中的“驱动”输入,因此如果您 const它离开你最终放弃了第一个输入。解决方法是 reshape 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

I've also seen in discussions here that there is no push based counterpart to >-> because it is easy to turn any pipe around (I assume with reflect?)


我不完全确定我的立场,但似乎并不完全适用于上述解决方案。现在我们可以正常工作,我们可以做的是使用 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

关于haskell - 如何将基于拉的管道变成基于推的管道?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40920498/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com