HSloggerを使用してプログラムに関する情報を取得しようとしています。だから私は私の関数に次の行を追加します
import Data.Word
import qualified Data.ByteString as B
import qualified Data.ByteString.Lazy as L
import Data.Bits
import Data.Int
import Data.ByteString.Parser
import System.Log.Logger
import System.Log.Handler.Syslog
importFile :: FilePath -> IO (Either String (PESFile ))
importFile n = do
warningM "MyApp.Component2" "Something Bad is about to happen."
...
関数はIO内にあるため、これは正常に機能します。ただし、次の関数に同様の行を追加すると、
...
parsePES :: Parser PESFile
parsePES = do
header <- string "#PES"
warningM "parsing header"
...
return (PESFile ...)
タイプエラーが発生します:
Couldn't match expected type `Parser a0'
with actual type `String -> IO ()'
In the return type of a call of `warningM'
In a stmt of a 'do' expression: warningM "parsing header"
In the expression:
do { header <- string "#PES";
warningM "parsing header";
...
そして、私は完全に理由を完全に理解しています-parsePESはIOモナドではなく、パーサーモナドにあります。私が理解していないのは、それについて何をすべきかです。スタックできるようにモナド変換器が必要ですか?パーサーモナドとIOモナドを一緒に?どうすればいいですか?
まず、簡単な免責事項:「ロギング」は、意味があるかどうかに関係なく、ある種の順次実行を想定しているため、通常、Haskellコードでは意味をなさない。 ログがプログラムの実行方法とログがどの値が計算されたかを区別することを確認してください。厳密な命令型言語ではこれらはほとんど同じですが、Haskellでは異なります。
そうは言っても、すでにシーケンシャルでステートフルな計算のコンテキストで、計算された値に基づいてログを記録したいようです。これは、他のほとんどの言語でのログとほぼ同じように機能します。ただし、そうするためのいくつかの手段をサポートするには、モナドが必要です。使用しているパーサーは HCodecsパッケージから のようです。これは比較的制限されているようで、IO
を許可せず、モナド変換子として定義されていません。
正直なところ、私のアドバイスは、異なる解析ライブラリの使用を検討することです。 Parsec はデフォルトの選択の一種である傾向があり、私は attoparsec が特定の目的(これにはあなたがやっていることを含むかもしれません)で人気があると思います。どちらを使用しても、ロギングをはるかに簡単に追加できます。Parsecはモナドトランスフォーマーであるため、IO
の上に配置し、必要に応じてliftIO
を使用できます。一方、attoparsecは増分処理を中心に設計されているため、処理(実際のパーサー内でのロギングはより厄介かもしれませんが)。他にも選択肢はありますが、推奨するための詳細が十分にわかりません。ほとんどのパーサーコンビネーターベースのライブラリは、かなり似通った設計になっている傾向があるため、コードの移植は簡単だと思います。
本当にあなたが持っているものにこだわりたい場合の最後のオプションは、現在使用している解析ライブラリの実装を見て、独自のIO
をロールすることです-その指向バージョン。しかし、それはおそらく理想的ではありません。
また、補足として、実際にログを記録しているのではなく、開発の一部としてプログラムの実行を追跡しているだけの場合、GHCiに組み込まれているデバッガーがより役立つか、古き良き Debug.Traceモジュール によるprintfデバッグ。
編集:わかりました、あなた自身のバリエーションをロールすることを検討するもっともらしい理由があるように聞こえます。ここでおおまかに必要なのは、ParserT
モナド変換子です。これがParser
の現在の定義です:
_newtype Parser a = Parser { unParser :: S -> Either String (a, S) }
_
タイプS
はパーサーの状態です。これはStateT S (Either String) a
のハードコーディングされたバージョンです。
_newtype StateT s m a = StateT { runStateT :: s -> m (a,s) }
_
...ここで_Either String
_はエラーモナドとして扱われています。 ErrorT
モナド変換子は同じことをします:
_newtype ErrorT e m a = ErrorT { runErrorT :: m (Either e a) }
_
したがって、現在の型がStateT S (ErrorT String Identity)
と等しい場合、必要なのはStateT S (ErrorT String IO)
になります。
モジュール内のほとんどの関数がParser
モナドの内部を混乱させていないように見えるので、型定義を単純に置き換え、適切な型を指定する必要がありますクラスインスタンスを作成し、独自のrunParser
関数を記述して、準備完了です。
免責事項:私は Logger haskellフレームワーク の作成者です。
McCannの回答は非常に詳細ですが、質問がなされた時点でHaskellには汎用のログフレームワークが欠けていたことがわかりません。 HSLoggerは現在標準になっていますが、非常に基本的なロギング機能を提供しますが、低速で拡張性がありません。明確にするために、HSLoggerのいくつかの欠陥を以下に示します。
WriterT
または他のソリューションを使用して、コードを混乱させないようにする必要があります。そうは言っても、 Logger haskell framework を紹介したいと思います。以下を含む、効率的で拡張可能なロギングを可能にします。
WriterT
モナドの実行と使用)TemplateHaskell
インターフェースを提供し、ファイル番号やモジュール名などの詳細をログに記録できますBaseLogger
の拡張機能として作成されますが、実用的な機能はありません。明確にするために-フィルタリング機能は、ロガートランスフォーマーとして20行未満で作成され、独自のトランスフォーマーを定義できます。その方法は ドキュメント で説明されています。ただし、ライブラリはかなり新しいため、必要な機能が不足している可能性があります。良い情報は、この機能を自分で簡単に作成できること、またはGitHubでリクエストを報告することで機能を改善できることです。
ロガーは私が働いている会社( luna-lang.org )によって内部的に開発されており、私たちが作成しているコンパイラーの内部で使用されています。
恥知らずなプラグイン:私はco-log
ロギングライブラリの作成者です。ライブラリの使用法と実装の詳細については、次のブログ投稿をご覧ください。
このライブラリの背後にある主なアイデアは、ロギングアクションを単純なHaskell関数として扱うことです。関数はHaskellの第一級市民であり、それらを扱うのは非常に簡単です。