このpipes
パッケージを使用すると、データ生成とデータ消費を分離できます。プログラムをログString
のプロデューサーとして作成し、実行時にそれらのログを使用する方法を選択しますString
。
たとえば、次の簡単なプログラムがあるとします。
import Control.Proxy
program :: (Proxy p) => () -> Producer p String IO r
program () = runIdentityP $ forever $ do
lift $ putStrLn "Enter a string:"
str <- lift getLine
respond $ "User entered: " ++ str
タイプは、それがProducer
s String
(この場合はログ文字列)であり、をIO
使用してコマンドを呼び出すこともできることを示していますlift
。したがってIO
、ロギングを伴わない通常のコマンドの場合は、を使用するだけlift
です。何かをログに記録する必要があるときはいつでも、respond
コマンドを使用して。を生成しString
ます。
これにより、文字列の消費方法を指定しない文字列の抽象的なプロデューサーが作成されます。これにより、生成されたを後で使用する方法の選択を延期することができますString
。コマンドを呼び出すときはいつでもrespond
、ログ文字列を抽象的に処理する未指定のダウンストリームステージに渡します。
次に、出力をファイルBool
に書き込むかどうかを指定するフラグをコマンドラインから取得するプログラムを作成しましょう。stdout
"my.log"
import System.IO
import Options.Applicative
options :: Parser Bool
options = switch (long "file")
main = do
useFile <- execParser $ info (helper <*> options) fullDesc
if useFile
then do
withFile "my.log" WriteMode $ \h ->
runProxy $ program >-> hPutStrLnD h
else runProxy $ program >-> putStrLnD
ユーザーがコマンドラインでフラグを指定しない場合、useFile
デフォルトFalse
はになり、にログを記録することを示しますstdout
。ユーザーが--file
フラグを指定すると、useFile
デフォルトTrue
はになり、ログに記録することを示します"my.log"
。
if
次に、2つのブランチを確認します。最初のブランチは、演算子を使用して、生成するString
sをprogram
ファイルにフィードします。を取り、各文字列をそのハンドルに書き込むsの抽象的なコンシューマーを作成するものと(>->)
考えてください。に接続すると、すべてのログ文字列がファイルに送信されます。hPutStrLnD
Handle
String
program
hPutStrLnD
$ ./log
Enter a string:
Test<Enter>
User entered: Test
Enter a string:
Apple<Enter>
User entered: Apple
^C
$
2番目のブランチはsをにif
フィードします。String
putStrLnD
stdout
$ ./log --file
Enter a string:
Test<Enter>
Enter a string:
Apple<Enter>
^C
$ cat my.log
User entered: Test
User entered: Apple
$
生成を本番から切り離しpipes
ても、すべてをすぐにストリーミングするため、出力ステージ(つまりhPutStrLnD
、 )は生成された直後にputStrLnD
書き込みを行い、 sをバッファリングしたり、プログラムが終了するまで待機したりしません。Strings
String
生成を実際のロギングアクションから切り離すことにより、最後の瞬間にコンシューマーの依存関係String
を注入できるようになることに注意してください。String
使用方法の詳細については、チュートリアルpipes
を読むことをお勧めします。pipes