The MaybeT
monad transformer would come handy in this particular case. MaybeT
monad transformer is just a type defined something like;
newtype MaybeT m a = MaybeT {runMaybeT :: m (Maybe a)}
Actually transformers like MaybeT
, StateT
etc, are readily available in Control.Monad.Trans.Maybe
, Control.Monad.Trans.State
... For illustration purposes it' Monad instance could be something like shown below;
instance Monad m => Monad (MaybeT m) where
return = MaybeT . return . Just
x >>= f = MaybeT $ runMaybeT x >>= g
where
g Nothing = return Nothing
g (Just x) = runMaybeT $ f x
so as you will notice the monadic f
function takes a value that resides in the Maybe
monad which itself is in another monad (IO
in our case). The f
function does it's thing and wraps the result back into MaybeT m a
.
Also there is a MonadTrans
class where you can have some common functionalities those are used by the transformer types. One such is lift
which is used to lift the value into a transformer according to that particular instance's definition. For MaybeT
it should look like
instance MonadTrans MaybeT where
lift = MaybeT . (liftM Just)
Lets perform your task with monad transformers.
addInts :: MaybeT IO ()
addInts = do
lift $ putStrLn "Enter two integers.."
i <- lift getLine
guard $ test i
j <- lift getLine
guard $ test j
lift . print $ (read i :: Int) + (read j :: Int)
where
test = and . (map isDigit)
So when called like
λ> runMaybeT addInts
Enter two integers..
1453
1571
3024
Just ()
The catch is, since a monad transformer is also a member of Monad
typeclass, one can nest them indefinitelly and still do things under a singe do
notation.
Edit: answer gets downvoted but it is unclear to me why. If there is something wrong with the approach please care to elaborate me so that it helps people including me to learn something better.
Taking the opportunity of being on the edit session, i would like to add a better code since i think Char
based test
ing might not be the best idea as it will not take negative Int
s into account. So let's try using readMaybe
from the Text.Read
package while we are doing things with the Maybe
type.
import Control.Monad.Trans.Maybe
import Control.Monad.Trans.Class (lift)
import Text.Read (readMaybe)
addInts :: MaybeT IO ()
addInts = do
lift $ putStrLn "Enter two integers.."
i <- lift getLine
MaybeT $ return (readMaybe i :: Maybe Int)
j <- lift getLine
MaybeT $ return (readMaybe j :: Maybe Int)
lift . print $ (read i :: Int) + (read j :: Int)
I guess now it works better...
λ> runMaybeT addInts
Enter two integers..
-400
500
100
Just ()
λ> runMaybeT addInts
Enter two integers..
Not an Integer
Nothing