Since you provided only a snippet of code, I cannot reorganize it. However, this is what I would do: most monads have an appropriate class type. The reason for this is exactly what you need here: when you create a monad using a monad transformer, it inherits the operations of the internal monads (if necessary). Thus, you can forget about the internal monads and work only in the final monad.
In your case, you have MaybeT IO . This is a copy of MonadPlus and MonadIO . This way you can reorganize the code that Maybe something returns to work with a common MonadPlus instance instead, just replace Just with return and Nothing with mzero . How:
-- before checkNumber :: Int -> Maybe Int checkNumber x | x > 0 = Just x | otherwise = Nothing x -- after checkNumber :: MonadPlus m => Int -> m Int checkNumber x | x > 0 = return x | otherwise = mzero -- or just: checkNumber = mfilter (> 0) . return
It will work with any MonadPlus , including Maybe and MaybeT IO .
And you can reorganize the code that returns IO something to work with a common MonadIO instance:
-- before doSomeIO :: IO () doSomeIO = getLine >>= putStrLn -- after doSomeIO :: MonadIO m => m () doSomeIO = liftIO $ getLine >>= putStrLn
So you can forget about <$> / fmap / liftM , Just , MaybeT , etc. You just use return , mzero and in some places liftIO .
It will also help you create more general code. If you later realize that you need to add something to the monad stack, existing code will not break if the new monad stack implements the same type classes.
source share