簡體   English   中英

如何將基於拉的管道轉換為基於推的管道?

[英]How to turn a pull based pipe into a push based one?

默認情況下,管道基於拉。 這是由於運算符>->通過+>>實現,該運算符是其pull類別的有意義的bind運算符。 我的理解是,這意味着如果您具有producer >-> consumer之類的代碼,則將首先調用消費者的主體,然后在等待數據后將調用生產者。

我在這里pipes文檔中看到 ,您可以使用Pipes.Core的代碼(reflect .)將基於拉的管道轉換為基於推式管道。 這意味着(如果我錯了,請糾正我)在producer >-> consumer上面的代碼中, 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))
 -- 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

加布里埃爾·岡薩雷斯(Gabriel 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

實際上,這是基於拉的,因為流是由下游Client reflect stdin驅動的:

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 

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM