Using Either as a Monad — Monday Morning Haskell (2024)

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:

  1. If the first letter of the string is capitalized, return the third letter. Otherwise, drop the first letter from the string.
  2. 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!

Using Either as a Monad — Monday Morning Haskell (2024)

FAQs

Is either a monad Haskell? ›

Also note that none of that should compile, because function names can't be capitalized. Finally, there is no such thing as "Monad data type": Either is an instance of the Monad type class.

What is the either function in Haskell? ›

In Haskell either is used to represent the possibility of two values. Either is used to represent two values that can be correct or error. It has two constructors also which are named Left and Right. These constructors also represent and show some purpose either in Haskell.

What is the difference between either and maybe in Haskell? ›

The Either type is similar to the Maybe type, but comes with some added functionality. It's also used to model things that can fail, but it provides a way to report what went wrong, not just that something failed. Either represents a piece of data that can be one of two things.

How to make a monad in Haskell? ›

To create a monad, it is not enough just to declare a Haskell instance of the Monad class with the correct type signatures. To be a proper monad, the return and >>= functions must work together according to three laws: (return x) >>= f ==== f x. m >>= return ==== m.

Are monads pure Haskell? ›

Every monad is entirely pure, because Haskell is a fully pure language. A monad is just a data type. It is just data that happens to satisfy a convenient interface.

Is A monad a Monoid? ›

In fact, monads are special cases of monoids, namely they are precisely the monoids among endofunctors. , which is equipped with the multiplication given by composition of endofunctors. Composition of monads is not, in general, a monad. For example, the double power set functor. does not admit any monad structure.

What is either function? ›

Either is used in functional programming to handle errors. Either is an alternative to try / catch / throw .

What does || do in Haskell? ›

The OR operator is used to evaluate two expressions. We can write a double pipe || to use the OR operator. It will return either a True or a False based on the conditions.

What does == mean in Haskell? ›

Numeric operators such as + are often defined to work on many different kinds of numbers. The equality operator (== in Haskell) usually works on numbers and many other (but not all) types.

Where should I use either? ›

Either is used when referring to a choice between two options. For example, “Either one deserves to win.” Or, “Either you leave, or I will phone the police.” It can also be used in a negative way, instead of the words also or too.

Is maybe a Monad Haskell? ›

We introduced monads using Maybe as an example. The Maybe monad represents computations which might "go wrong" by not returning a value.

What are the rules for either? ›

Either is used before the first of two or more options or to indicate a link with another statement. Either can also indicate one or the other of two people or things. Neither is used before the first of two options to signal that they are untrue or won't occur. It can also be used to emphasize a negative statement.

What is a monad for dummies? ›

In functional programming, a monad is a structure that combines program fragments (functions) and wraps their return values in a type with additional computation.

What the heck is a monad? ›

A monad, fundamentally, defines how functions are composed in the presence of an associated effect. A monad is made up of two operators, often called unit and bind , and often given in Haskell syntax like the following, given a monad m . unit :: a -> m a bind :: (a -> m b) -> (m a -> m b)

Is a monad just a wrapper? ›

No, monads are not wrappers. Some wrappers are monads, but not all. inb4, monads are not pipes either. Sure, a monad is a type constructor.

Is maybe a monad Haskell? ›

We introduced monads using Maybe as an example. The Maybe monad represents computations which might "go wrong" by not returning a value.

What is a monad class in Haskell? ›

The Monad class

Monads can be viewed as a standard programming interface to various data or control structures, which is captured by Haskell's Monad class. All the common monads are members of it: class Monad m where (>>=) :: m a -> ( a -> m b) -> m b (>>) :: m a -> m b -> m b return :: a -> m a.

Is every functor a monad? ›

There is a deeper relationship between functors and monads. It turns out that every monad is a functor. Another way to say this is that the monad abstraction is more powerful than the functor abstraction, because not every functor is a monad.

Is Io a monad Haskell? ›

The I/O monad constitutes a small imperative sub-language inside Haskell, and thus the I/O component of a program may appear similar to ordinary imperative code.

References

Top Articles
Latest Posts
Article information

Author: Nathanael Baumbach

Last Updated:

Views: 5775

Rating: 4.4 / 5 (75 voted)

Reviews: 82% of readers found this page helpful

Author information

Name: Nathanael Baumbach

Birthday: 1998-12-02

Address: Apt. 829 751 Glover View, West Orlando, IN 22436

Phone: +901025288581

Job: Internal IT Coordinator

Hobby: Gunsmithing, Motor sports, Flying, Skiing, Hooping, Lego building, Ice skating

Introduction: My name is Nathanael Baumbach, I am a fantastic, nice, victorious, brave, healthy, cute, glorious person who loves writing and wants to share my knowledge and understanding with you.