Now that February is over and we're into March, it's time for "Monads Month"! Over the course of the next month I'll be giving some helpful tips on different ways to use monads.

Today I'll start with a simple observation: the `Either`

type is a monad! For a long time, I used `Either`

as if it were just a normal type with no special rules. But its monadic behavior allows us to chain together several computations with it with ease!

Let's start from the beginning. What does `Either`

look like? Well, it's a very basic type that can essentially hold one of two types at runtime. It takes two type parameters and has two corresponding constructors. If it is "Left", then it will hold a value of the first type. If it is "Right" then it will hold a value of the second type.

`data Either a b = Left a | Right b`

A common semantic understanding of `Either`

is that it is an extension of `Maybe`

. The `Maybe`

type allows our computations to either succeed and produce a `Just`

result or fail and produce `Nothing`

. We can follow this pattern in `Either`

except that failures now produce some kind of object (the first type parameter) that allows us to distinguish different kinds of failures from each other.

Here's a basic example I like to give. Suppose we are validating a user registration, where they give us their email, their password, and their age. We'll provide simple functions for validating each of these input strings and converting them into `newtype`

values:

`newtype Email = Email Stringnewtype Password = Password Stringnewtype Age = Age IntvalidateEmail :: String -> Maybe EmailvalidateEmail input = if '@' member input then Just (Email input) else NothingvalidatePassword :: String -> Maybe PasswordvalidatePassword input = if length input > 12 then Just (Password input) else NothingvalidateAge :: String -> Maybe AgevalidateAge input = case (readMaybe input :: Maybe Int) of Nothing -> Nothing Just a -> Just (Age a)`

We can then chain these operations together using the monadic behavior of `Maybe`

, which short-circuits the computation if `Nothing`

is encountered.

`data User = User Email Password AgeprocessInputs :: (String, String, String) -> Maybe UserprocessInputs (i1, i2, i3) = do email <- validateEmail i1 password <- validatePassword i2 age <- validateAge i3 return $ User email password age`

However, our final function won't have much to say about *what* the error was. It can only tell us that an error occurred. It can't tell us which input was problematic:

`createUser :: IO (Maybe User)createUser = do i1 <- getLine i2 <- getLine i3 <- getLine result <- processInputs (i1, i2, i3) case result of Nothing -> print "Couldn't create user from those inputs!" >> return Nothing Just u -> return (Just u)`

We can extend this example to use `Either`

instead of `Maybe`

. We can make a `ValidationError`

type that will help explain which kind of error a user encountered. Then we'll update each function to return `Left ValidationError`

instead of `Nothing`

in the failure cases.

`data ValidationError = BadEmail String | BadPassword String | BadAge String deriving (Show)validateEmail :: String -> Either ValidationError EmailvalidateEmail input = if '@' member input then Right (Email input) else Left (BadEmail input)validatePassword :: String -> Either ValidationError PasswordvalidatePassword input = if length input > 12 then Right (Password input) else Left (BadPassword input)validateAge :: String -> Either ValidationError AgevalidateAge input = case (readMaybe input :: Maybe Int) of Nothing -> Left (BadAge input) Just a -> Right (Age a)`

Because `Either`

**is a monad** that follows the same short-circuiting pattern as `Maybe`

, we can also chain these operations together. Only now, the result we give will have more information.

`processInputs :: (String, String, String) -> Either ValidationError UserprocessInputs (i1, i2, i3) = do email <- validateEmail i1 password <- validatePassword i2 age <- validateAge i3 return $ User email password agecreateUser :: IO (Either ValidationError User)createUser = do i1 <- getLine i2 <- getLine i3 <- getLine result <- processInputs (i1, i2, i3) case result of Left e -> print ("Validation Error: " ++ show e) >> return e Right u -> return (Right u)`

Whereas `Maybe`

gives us the monadic context of "this computation may fail", `Either`

can extend this context to say, "If this fails, the program will give you an error why."

Of course, it's not mandatory to view `Either`

in this way. You can simply use it as a value that could hold two arbitrary types with no error relationship:

`parseIntOrString :: String -> Either Int StringparseIntOrString input = case (readMaybe input :: Maybe Int) of Nothing -> Right input Just i -> Left i`

This is completely valid, you just might not find much use for the `Monad`

instance.

But you might find the monadic behavior helpful by making the `Left`

value represent a successful case. Suppose you're writing a function to deal with a multi-layered logic puzzle. For a simple example:

- If the first letter of the string is capitalized, return the third letter. Otherwise, drop the first letter from the string.
- If the third letter in the remainder is an 'a', return the final character. Otherwise, drop the last letter from the string.3 (and so on with similar rules)

We can encode each rule as an `Either`

function:

`rule1 :: String -> Either Char Stringrule1 input = if isUpper (head input) then Left (input !! 2) else Right (tail input)rule2 :: String -> Either Char Stringrule2 input = if (input !! 2 == 'a') then Left (last input) else Right (init input)rule3 :: String -> Either Char String...`

To solve this problem, we can use the `Either`

monad!

`solveRules :: String -> Either Char StringsolveRules input = do result1 <- rule1 input result2 <- rule2 result1 ...`

If you want to learn more about monads, you should check out our blog series! For a systematic, in depth introduction to the concept, you can also take our Making Sense of Monads course!