Standardmäßig sind Pipes Pull-basiert. Dies ist auf den Operator zurückzuführen, der über +>>
implementiert wird, der der sinnvolle Operator bind
für seine Pull-Kategorie ist. Mein Verständnis ist, dass dies bedeutet, dass, wenn Sie Code wie producer >-> consumer
haben, der Körper des Verbrauchers zuerst genannt wird, dann, sobald es Daten erwartet, wird der Produzent angerufen.Wie wird aus einem Pull-basierten Rohr ein Push-basiertes Rohr gemacht?
Ich habe in der pipes
Dokumentation gesehen here, dass Sie den Code (reflect .)
von Pipes.Core
können einen Zug auf Basis Rohr in ein Push-basierten Rohr zu drehen. Das heißt stattdessen (korrigiere mich, wenn ich falsch liege), dass im Code über producer >-> consumer
der Produzent zuerst ausgeführt wird, einen Wert produziert, dann versucht der Konsument zu konsumieren. Das scheint wirklich nützlich zu sein und ich würde gerne wissen, wie es geht.
Ich habe auch in den Diskussionen here gesehen, dass es kein Push-basiertes Gegenstück zu gibt, weil es einfach ist, jedes Rohr herum zu drehen (ich nehme an mit?), Aber ich kann nicht wirklich herausfinden, wie man es macht oder finde irgendwelche Beispiele.
Hier einige Code, den ich versucht habe:
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))