I have some code that currently uses a ST monad for evaluation. I like not putting IO everywhere because the runST
method produces a pure result, and indicates that such result is safe to call (versus unsafePerformIO
). However, as some of my code has gotten longer, I do want to put debugging print statements in.
Is there any class that provides a dual-personality monad [or typeclass machinery], one which can be a ST or an IO (depending on its type or a "isDebug" flag)? I recall SPJ introduced a "Mutation" class in his "Fun with Type Functions" paper, which used associative types to relate IO to IORef and ST to STRef. Does such exist as a package somewhere?
Edit / solution
Thanks very much [the nth time], C.A. McCann! Using that solution, I was able to introduce an additional class for monads which support a pdebug
function. The ST
monad will ignore these calls, whereas IO
will run putStrLn
.
class DebugMonad m where
pdebug :: String -> m ()
instance DebugMonad (ST s) where
pdebug _ = return ()
instance DebugMonad IO where
pdebug = putStrLn
test initV = do
v <- newRef initV
modifyRef v (+1)
pdebug "debug"
readRef v
testR v = runST $ test v
This has a very fortunate consequence in ghci. Since it expects expressions to be IO types by default, running something like "test 3" will result in the IO monad being run, so you can debug it easily, and then call it with something like "testR" when you actually want to run it.
seq (somecomp somedata) ()
into ghci; the first time it prints debug and an error message, the second time just the error. of course,:r
works, but I'd rather it have better semantics [i.e. Debug.Trace is kind of a hack]. – Birk