Writings about the things I learn (Ruby / Haskell / Web / Other Stuff)
Playing With the Either Monad in Haskell
After playing with the bitcoin price fetcher, I was disappointed at how… hard
it was to deal with the multiple layers of potential errors. I started looking
into the errors package on Hackage for a way out. It is a one-stop-shop for
all the standard error handling mechanisms in Haskell. It reexports the
standard Either and Maybe types, and also adds many helper functions to move
between Either and Maybe types, in addition to helping out with the various
transformer versions of both (MaybeT and EitherT)
I will play with MaybeT and EitherT later, for now I’m happy to have figured out
the Either monad, and want to share the annotated example I’ve cobbled together.
Grab the code into a file, cabal install errors, and start toying with the
various places I use the Pass and Error types in the doWork function.
You’ll see how nicely Haskell handles a long string of things, where any one of
them could fail out.
I’ll have to go rewrite the bitcoin scraper with my newfound knowledge…
importControl.Error-- A type for my example functions to pass or fail on.dataFlag=Pass|Errormain::IO()main=doputStrLn"Starting to do work:"letresult=doWorkcaseresultofLeftval->putStrLn$"Work Result: Failed\n "++valRightval->putStrLn$"Work Result: Passed\n "++valputStrLn"Ok, finished. Have a nice day"-- This is a driver function, simulating an error prone path-- through the app. Each step could possibly error out, and-- when any of them do, we want to just bail out.---- Remember the definition of the Either monad is:-- instance Monad (Either e) where-- return = Right-- Right m >>= k = k m-- Left e >>= _ = Left e---- So a Left value short circuits the rest of the Monad, and a Right value-- passes the value off to the next step.doWork::EitherStringStringdoWork=do-- use do notation syntax sugar for the Either monad-- First, do something that may or may not work. We get back a type of-- Either String String (since that's the type of the example-- eitherFailure function here)x<-eitherFailurePass"Initial Thing"-- Based on what we get in x, just go ahead and attempt it.-- Note that the function eitherFailure takes a simple-- String as its argument. So we didn't have to unwrap the-- first Either value.y<-eitherFailureError("Second Thing "++x)-- We can't just wire a Maybe value in the middle here,-- since it doesn't typecheck. (Maybe isn't an Either),-- even though they play similarly. If we just tried, we'd get:-- z <- maybeFailure Error-- Couldn't match type `Maybe' with `Either String'-- But instead, we can use Control.Error.Util.note to convert-- an "empty" Nothing value into a Left value with a descriptive-- error. So now we'd get a proper Either value we can chain-- into this overall monad.note("Failed the Maybe: "++y)$maybeFailurePassy-- Since the last line of this `do` block is the type we plan on-- returning, there's no `return` call needed.-- 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->EitherStringStringeitherFailurePassval=Right$"-> Passed "++valeitherFailureErrorval=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->MaybeStringmaybeFailurePassval=Just$"-> Passed maybe "++valmaybeFailureError_=Nothing
My favorite part of diving into the error library is that my worry from yesterday,
except then I’d have to switch the Maybe result out of Lens-Aeson into a “fuller” Either type. is just the note function I demoed above.