この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
タイプは、それがProducers 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つのブランチを確認します。最初のブランチは、演算子を使用して、生成するStringsをprogramファイルにフィードします。を取り、各文字列をそのハンドルに書き込むsの抽象的なコンシューマーを作成するものと(>->)考えてください。に接続すると、すべてのログ文字列がファイルに送信されます。hPutStrLnDHandleStringprogramhPutStrLnD
$ ./log
Enter a string:
Test<Enter>
User entered: Test
Enter a string:
Apple<Enter>
User entered: Apple
^C
$
2番目のブランチはsをにifフィードします。StringputStrLnDstdout
$ ./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をバッファリングしたり、プログラムが終了するまで待機したりしません。StringsString
生成を実際のロギングアクションから切り離すことにより、最後の瞬間にコンシューマーの依存関係Stringを注入できるようになることに注意してください。String
使用方法の詳細については、チュートリアルpipesを読むことをお勧めします。pipes