Dav*_*aly 6 haskell haskell-pipes
默认情况下,管道是基于拉的.这是由于实施的操作员>->,通过+>>该bind操作员是他的拉动类别的有意义的操作员.我的理解是,这意味着如果你有代码producer >-> consumer,首先会调用消费者的身体,然后一旦等待数据,就会调用生产者.
我见过的在pipes文档在这里,你可以使用代码(reflect .)从Pipes.Core把一个基于拉管道进入基于推送管.这意味着(纠正我,如果我错了)在上面的代码中producer >-> 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))
Run Code Online (Sandbox Code Playgroud)
Run Code Online (Sandbox Code Playgroud)-- 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
Run Code Online (Sandbox Code Playgroud)
... 和:
GHCi> runEffect (stdin >>~ (\_ -> countLetters))
stdin
foo
countLetters
stdin
glub
4
countLetters
stdin
Run Code Online (Sandbox Code Playgroud)
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'
Run Code Online (Sandbox Code Playgroud)
GHCi> runEffect (stdin >>~ countLettersPush)
stdin
foo
countLetters
3
stdin
glub
countLetters
4
stdin
Run Code Online (Sandbox Code Playgroud)
我还在此处的讨论中看到,没有基于推送的对应项,
>->因为很容易扭转任何管道(我假设使用反射?)
我不完全确定我的立场,但似乎并不完全适用于上面的解决方案。既然我们已经使基于推送的流程正常工作,我们能做的就是将其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
Run Code Online (Sandbox Code Playgroud)
这确实是基于拉动的,因为流量是由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
Run Code Online (Sandbox Code Playgroud)
然而,该流涉及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
Run Code Online (Sandbox Code Playgroud)