Haskell -- dual personality IO / ST monad?
Asked Answered
B

2

18

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.

Birk answered 26/7, 2011 at 22:24 Comment(0)
G
17

If you want a unified interface to IORef and STRef, have you looked at the stateref package? It has type classes for "references to mutable data", separated for readable, writable, etc., with instances for IORef and STRef, as well as things like TVar, MVar, ForeignPtr, etc.

Gusta answered 26/7, 2011 at 22:50 Comment(0)
D
10

Have you considered Debug.Trace.trace instead?

http://www.haskell.org/haskellwiki/Debugging

Distend answered 26/7, 2011 at 22:29 Comment(1)
I am currently using that. However, it has its weaknesses, such as the trace results only being printed once: after a variable has a value (e.g. the result of the ST computation), it's not recomputed. Example: I type 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

© 2022 - 2024 — McMap. All rights reserved.