Watch Chris Learn!

Writings about the things I learn (Ruby / Haskell / Web / Other Stuff)

Working Entirely in EitherT

This is the last post in my series of stuff on the Either monad.

  1. Playing with the Either Monad
  2. Using the Either Monad Inside Another Monad
  3. EitherT Inside of IO

It’s a smallish change to the code, where I get rid of a lot of the annoying casting code to go Either -> EitherT, and instead just write everything in EitherT.

The biggest change was the type signature of my failure code. See how I add the Monad constraint, and update the return value to be EitherT wrapped around whatever monad you have.

What’s cool about this is that it’ll work right for both IO, and every other monad we want to embed this eitherFailure code into. Which means that as a hypothetical application’s monad transformer stack builds up, it would be easy to just plug this code in and go.

1
2
3
eitherFailure :: Monad m => Flag -> String -> EitherT String m String
eitherFailure Pass  val = right $ "-> Passed " ++ val
eitherFailure Error val = left  $ "-> Failed " ++ val

One other gotcha is that I had to change Right to right, which is a function that returns a hoisted version of the Either value. No biggie, just wouldn’t typecheck till I did.

If you read this code, you’ll see that the transformation from Maybe to MaybeT is very similar, right down to using just and nothing as functions, rather than the Just and Nothing data constructors.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import Control.Error
import Control.Monad.Trans

-- A type for my example functions to pass or fail on.
data Flag = Pass | Error

main :: IO ()
main = do
  putStrLn "Starting to do work:"

  result <- runEitherT $ do
      lift $ putStrLn "Give me the first input please:"
      initialText <- lift getLine
      x <- eitherFailure Error initialText

      lift $ putStrLn "Give me the second input please:"
      secondText <- lift getLine
      y <- eitherFailure Pass (secondText ++ x)

      noteT ("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"

eitherFailure :: Monad m => Flag -> String -> EitherT String m String
eitherFailure Pass  val = right $ "-> Passed " ++ val
eitherFailure Error val = left  $ "-> Failed " ++ val

maybeFailure :: Monad m => Flag -> String -> MaybeT m String
maybeFailure Pass  val = just $ "-> Passed maybe " ++ val
maybeFailure Error _   = nothing

Comments