9

状態モナドの変化を追跡したい。これは動作しません:

main :: IO ()
main = do
    print $ snd $ execState compute initialState

traceThis :: (Show a) => a -> a
traceThis x = trace ("test: " ++ show x) x

compute :: State ([Row], Integer) String
compute = liftM traceThis $ get >>= \(rs, result) -> put (rs, result + 3) >> return "foo"

何も出力されません (正しく更新されたメイン関数での出力の最終結果を除く)。

状態を追跡するためのアイデアや代替案はありますか? これを使用して、プロジェクトのオイラー ソリューションの正確性を確認したいと考えています。

4

3 に答える 3

12

あなたの場合の問題は、traceThis決して評価されないことです。Haskell は遅延言語なので、必要な式だけを評価します。また、計算結果は評価せず、状態のみを評価するため、traceThis内部で評価する必要はありませんcompute。たとえば、印刷する場合

print $ evalState compute initialState

次に、ステートフルな計算の結果の値が、呼び出しとともに評価されtraceThisます。

より良いオプションは、モナド計算のいずれかの部分が評価されるたびに結果値を出力することを強制するモナド関数を定義することです:

traceState :: (Show a) => a -> State s a
traceState x = state (\s -> trace ("test: " ++ show x) (x, s))

compute :: State ([Int], Integer) String
compute = get >>= \(rs, result) -> put (rs, result + 3)
              >> return "foo"
              >>= traceState

更新:これは任意のモナドに一般化できます。要点は、内部の値が評価されるかどうかに関係なく、 が評価traceされるときに評価されるように、内部の値だけでなくモナド計算をラップする必要があるということです。>>=

traceMonad :: (Show a, Monad m) => a -> m a
traceMonad x = trace ("test: " ++ show x) (return x)
于 2012-08-07T12:49:22.783 に答える
5

これが代替案です。StateT s IOあなたのモナドとして使用してください:

compute :: StateT ([Row], Integer) IO String
compute = do
    (rs, result) <- get
    lift $ putStrLn "result = " ++ show result
    put (rs, result + 3)
    return "foo"

IOを使用して、アクションを任意の場所にインターリーブできるようになりliftました。

モナドトランスフォーマーについてさらに学ぶには、優れた紹介であるMonad Transformers - Step by Stepを読むことをお勧めします。

于 2012-08-07T12:40:45.057 に答える
5

を呼び出すときはexecState、関数によって返される値ではなく、最終的な状態を求めているだけですcomputeliftM一方、状態に触れないモナドtraceThisのアクションに関数を持ち上げます。Stateしたがって、怠惰のため、 によって返されたtraceThisを強制的に評価する場合にのみ呼び出されます。一般に、 が適切に機能するには、呼び出す値が評価されることを確認する必要があります。computetrace

Debug.Trace通常、迅速なデバッグにのみ適しています。非常に強力なログ システムではなく、怠惰のために使いにくい場合があります。これをより確実に行う方法を探している場合は、状態タプルに別の要素 (おそらく文字列のリスト) を追加し、compute関数にログ メッセージを書き込むようにすることができます。

于 2012-08-07T12:44:57.480 に答える