Written by
Chris Schneider
on
on
Using the Either monad inside another monad
After yesterday’s post about the
Either Monad
I wanted to see if it was easy to embed that bit of doWork
stuff right
into the main function.
This was mostly about learning the syntax, I would suggest keeping stuff separate as much as possible in real code.
The biggest gotcha I found was that the indentation of the x <-
eitherFailure...
bit needed to be deeper than the r
in the result
token.
This ended up being more than my normal 2 space indent.
import Control.Error
-- A type for my example functions to pass or fail on.
data Flag = Pass | Error
main :: IO ()
main = do
putStrLn "Starting to do work:"
-- The inner monad here is Either. But note that we have
-- no IO ability inside of it.
let result = do
x <- eitherFailure Pass "Initial Thing"
y <- eitherFailure Error ("Second Thing " ++ x)
note ("Failed the Maybe: " ++ y) $ maybeFailure Pass y
case result of
Left val -> putStrLn $ "Work Result: Failed\n " ++ val
Right val -> putStrLn $ "Work Result: Passed\n " ++ val
putStrLn "Ok, finished. Have a nice day"
-- Simple function that we can use to force it to error out with a Left, or
-- pass with a Right value. It just includes some helper text as its content,
-- showing what happened.
eitherFailure :: Flag -> String -> Either String String
eitherFailure Pass val = Right $ "-> Passed " ++ val
eitherFailure Error val = Left $ "-> Failed " ++ val
-- Simlar to eitherFailure, but return a (Just String) or a Nothing based on
-- if we told it to fail.
maybeFailure :: Flag -> String -> Maybe String
maybeFailure Pass val = Just $ "-> Passed maybe " ++ val
maybeFailure Error _ = Nothing
You can see it’s the same code, except the result
in main is calculated
directly there, rather than calling another function.
Note that this isn’t the transformer library, so you can’t be clever and do
stuff like lift
and friends to do IO in that Either workflow.