I want to keep track of changes in the state monad. This does not work:
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"
Nothing gets printed (except the end result from the print in the main function which has correctly been updated).
Any ideas or alternatives to track state? I want to use this for checking correctness of a project euler solution.
The problem in your case is that traceThis
is never getting evaluated. Haskell is a lazy language, so it only evaluates expressions that are needed. And since you don't evaluate computation result, only the state, it's not necessary to evaluate traceThis
inside compute
. If you print for example
print $ evalState compute initialState
then the result value of the stateful computation gets evaluated along with calling traceThis
.
A better option would be to define a monadic function that forces printing the result value whenever any part of the monadic computation is evaluated:
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
Update: This can be generalized to an arbitrary monad. The main point is that trace
must wrap the monadic computation, not just the value inside, so that it gets evaluated when >>=
is evaluated, regardless of whether the value inside is evaluated or not:
traceMonad :: (Show a, Monad m) => a -> m a
traceMonad x = trace ("test: " ++ show x) (return x)
Here's an alternative. Use StateT s IO
as your monad:
compute :: StateT ([Row], Integer) IO String
compute = do
(rs, result) <- get
lift $ putStrLn "result = " ++ show result
put (rs, result + 3)
return "foo"
Now you can interleave IO
actions anywhere using lift
.
To learn more about monad transformers, I recommend you read the excellent introduction: Monad Transformers - Step by Step.
When you call execState
, you are just asking for the final state, not for the value returned by the compute
function. liftM
, on the other hand, lifts your traceThis
function to an action in the State
monad that doesn't touch the state. Thus, due to laziness, traceThis
will only be called if you force the value returned by compute
to be evaluated. In general, for trace
to work properly, you have to be sure that the value that you call it on gets evaluated.
Debug.Trace
is generally only suitable for quick debugging - it is not a very powerful logging system and can be difficult to use due to laziness. If you are looking for a way to do this more robustly, you could add another element (perhaps a list of strings) to your state tuple and have the compute
function write log messages to that.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With