I have this code in Haskell:
import Control.Monad.Trans.State
simpleState = state (\x -> (x, x + 1))
runUntil :: (s -> Bool) -> State s a -> State s a
runUntil f s = do
s' <- get
-- Here I want to print value of s' to console
if f s'
then s >> runUntil f s
else s
main :: IO ()
main = do
let (x,s) = runState (runUntil (< 10) simpleState) 0
putStrLn $ "State = " ++ (show s) ++ " Result = " ++ (show x)
I want to print the value of the state on each iteration of runUntil.
If I can't print it in runUntil
function where I can do this?
Welcome to the wonderful world of Monad Transformers. There's a nice library called the MTL that provides the "monad transformer" equivalents of most monads. By convention, these end with a capital T, so StateT
is what we want. Monad transformers have their usual operations and one more, lift
, for a StateT
that looks like this,
lift :: Monad m => m a -> StateT s m a
Now there's a special class for transformers on top of IO
called MonadIO
. To use it, we'd do something like. It's similar to just a plain old monad transformer but has the type signature
liftIO :: (MonadIO m, Monad m) => IO a -> m a
import Control.Monad.State
import Control.Monad.Trans
simpleState :: StateT Integer IO ()
simpleState = modify (+1)
runUntil :: Show s => (s -> Bool) -> StateT s IO a -> StateT s IO s
runUntil pred newState = do
curr <- get
if pred curr
then liftIO (print curr) >> newState >> runUntil pred newState
else return curr
Then to run it, there's a handy set of functions that turn StateT s m a
's into s -> (s, a)
.
main :: IO ()
main = do
(x,s) <- runStateT (runUntil (< 10) simpleState) 0
putStrLn $ "State = " ++ (show s) ++ " Result = " ++ (show x)
Notice that now we use bind (the <-
) because the result is in IO
, it's no longer pure. Monad transformers can be pretty confusing, luckily Real World Haskell has a chapter on them. If you're confused it's worth looking at.
It looks to me like our print might just be for debugging purposes....
If I am correct, you can print to the terminal from anywhere (even outside of an IO function) using Debug.trace.
Just import Debug and connect to any other value like this
trace (show s') $
if f s'
then s >> runUntil f s
else s
(note the signature of trace is String->a->a, so you need the $ followed by another expression
Again, this is only for development code and should be removed from production code (it totally breaks the whole "no side effects" thing)!
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