How to invest monads

I have this code in Haskell:

import Control.Monad.Trans.State simpleState = state (\x -> (x, x + 1)) runUntil :: (s -> Bool) -> State sa -> State sa runUntil fs = do s' <- get -- Here I want to print value of s' to console if fs' then s >> runUntil fs 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 a status value on each runUntil iteration.
If I cannot print it in runUntil , where can I do it?

+7
haskell monads
source share
2 answers

Welcome to the wonderful world of Monad Transformers. There's a good library called MTL that provides the equivalents of the “monad transformer" of most monads. By convention, they end in capital T, so StateT is what we want. Monad transformers have their usual operations and another, lift , for StateT , which looks like this:

 lift :: Monad m => ma -> StateT sma 

Now there is a special class for transformers on top of the IO , called MonadIO . To use it, we would do something like this. It looks like a simple old monad transformer, but has a signature like

 liftIO :: (MonadIO m, Monad m) => IO a -> ma 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 is a convenient set of functions that turn StateT sma into s -> (s, a) .

 main :: IO () main = do (x,s) <- runStateT (runUntil (< 10) simpleState) 0 putStrLn $ "State = " ++ (show s) ++ " Result = " ++ (show x) 

Note that we are now using bind ( <- ), because the result is in IO , it is no longer pure. Monad transformers can be quite confusing, fortunately Real World Haskell has a chapter about them. If you're confused, it's worth a look.

+10
source share

It seems to me that our printing can only be for debugging purposes ....

If I'm right, you can print to the terminal from anywhere (even outside the I / O function) using Debug.trace.

Just import Debug and connect to any other value like this

 trace (show s') $ if fs' then s >> runUntil fs else s 

(note that the trace signature is String-> a-> a, so you need $ followed by another expression

Again, this is only for development code and should be removed from the production code (it completely destroys all the “side effects”)!

+3
source share

All Articles