次のようなソースレベル関数に対してこれを行う場合:
myFoo x y = x + y
コンパイラでハッキングしたい場合を除いて、あなたはほとんど運が悪いです。ただし、適切な注釈を使用して、これをサポートする関数の独自の概念を定義できます。この概念を a と呼びましょうUserAction a
。ここa
で、 はアクションの戻り値の型です。で計算を構成するにはUserAction
、Monad
. あまり難しく考えなくても、私の第一印象は、このモナド変換子のスタックを使用することです。
type UserAction = WriterT [LogEntry] (ReaderT FuncIdentifier IO)
このWriterT [LogEntry]
コンポーネントは、 aUserAction
を実行すると、データベースに書き込む情報を含む一連のLogEntry
s [1] が生成されることを示しています。何かのようなもの:
data LogEntry = Call FuncIdentifier FuncIdentifier
今のところ、ランダム シード、タスク ID などの保存を延期しても問題ありません。情報を に追加することで、この設計に組み込むことができますLogEntry
。
ReaderT FuncIdentifier
コンポーネントは、 a が aUserAction
に依存していると述べていFuncIdentifier
ます。つまり、それを呼び出している関数の識別子です。
FuncIdentifier
次のような簡単なもので実装できます
type FuncIdentifier = String
または、必要に応じて、より構造のあるものを使用します。
IO
コンポーネントは、sUserAction
がファイル、コンソール、スポーン スレッドなど、すべてに対して任意の入出力を実行できることを示しています。アクションでこれが必要ない場合は、使用しないでください (Identity
代わりに使用してください)。しかし、あなたが乱数の生成について言及したので、純粋な計算を念頭に置いていないと思いました[2]。
次に、次のような関数を使用して、ログを記録する各アクションに注釈を付けます。
userAction :: FuncIdentifier -> UserAction a -> UserAction a
次のように使用されます。
randRange :: (Integer, Integer) -> UserAction Integer
randRange (low,hi) = userAction "randRange" $ do
-- implementation
userAction
通話を録音し、通話を録音するように呼び出し先を設定します。例:
userAction func action = do
caller <- ask
-- record the current call
tell [Call caller func]
-- Call the body of this action, passing the current identifier as its caller.
local (const func) action
最上位から目的のアクションを実行し、それが終了したら、すべての を収集しLogEntry
てデータベースに書き込みます。
コードの実行中に呼び出しをリアルタイムで記述する必要がある場合は、別のUserAction
モナドが必要になります。ただし、同じインターフェイスを表示することはできます。
このアプローチでは、モナド変換子などの中間的な Haskell 概念を使用します。IRC to irc.freenode.net
#haskell
channel にアクセスして、この実装スケッチの詳細を記入するためのガイダンスを求めることをお勧めします。彼らは親切な人たちで、あなたが学ぶのを喜んで助けてくれます :-)。
[1] 実際には、パフォーマンスのために使用するの[LogEntry]
ではなく、使用したいと思うでしょう。しかし、変更は簡単です。Haskell に慣れるまでは を使用し、それから に切り替えることをDList LogEntry
お勧めします。[LogEntry]
DList
[2] 乱数の生成は純粋に行うことができますが、このスケッチにはすでに多くの脳の再配線が必要なので、それをIO
効果として扱うことをお勧めします。