6

デフォルトでは、パイプはプル ベースです。これは、彼のプル カテゴリのポイントフルオペレータである>->経由で実装されたオペレータによるものです。私の理解では、これは、のようなコードがある場合、コンシューマーの本体が最初に呼び出され、データを待機するとプロデューサーが呼び出されることを意味します。+>>bindproducer >-> 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表現することはできません。(>->)

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 に答える