This article was originally posted on the on February 13th, 2017. Check out for more Haskell content! Monday Morning Haskell blog the blog We should now have a decent grasp on and (check out the links if you aren’t!). Now it’s time to take the next step up. We’re going to tackle the dreaded concept of . There are dozens of monad tutorials and descriptions on the internet. This makes sense. Monads are to writing any kind of meaningful program in Haskell. They aren’t the hardest concept in functional programming, but they are the because of their importance. In this series of articles, we’re going to try tackling the concept in small, manageable chunks. functors applicative functors Monads vital biggest roadblock So without further ado, here’s my crack at a definition: A Monad wraps a value or a computation with a particular . A monad must define both a means of normal values in the context, and a way of within the context. context wrapping combining computations This definition is quite broad. So let’s look at a more practical level to try to make sense of this. The Monad Typeclass Just like with functors and applicative functors, Haskell represents monads with a type class. It has two functions: class Monad m where return :: a -> m a (>>=) :: m a -> a -> m b -> m b These two functions correspond to the two ideas from above. The function specifies a how to values in the monad’s context. The operator, which we call the “bind” function, specifies how to two operations within the context. Let’s clarify this further by exploring a few specific monad instances. return wrap >>= combine The Maybe Monad Just as is a functor and an applicative functor, it is also a monad. To motivate the monad, let’s consider this code. Maybe Maybe maybeFunc1 :: String -> Maybe IntmaybeFunc1 “” = NothingmaybeFunc1 str = Just $ length str maybeFunc2 :: Int -> Maybe FloatmaybeFunc2 i = if i `mod` 2 == 0 then Nothing Else Just ((fromIntegral i) * 3.14159) maybeFunc3 :: Float -> Maybe [Int]maybeFunc3 f = if f > 15.0 then Nothing else $ Just [floor f, ceil f] runMaybeFuncs :: String -> Maybe [Int]runMaybeFuncs input = case maybeFunc1 input of Nothing -> Nothing Just i -> case maybeFunc2 i of Nothing -> Nothing Just f -> maybeFunc3 f We can see we’re starting to develop a hideous triangle pattern as we continue pattern matching on the results of successive function calls. If we were to add more functions onto this, it would . When we consider as a monad, we can make the code much cleaner. Let’s take a look at how Haskell implements as a monad to see how. Maybe keep getting worse Maybe Maybe instance Monad Maybe where return = Just Nothing >>= _ = Nothing Just a >>= f = f a The context the Maybe monad describes is simple. Computations in Maybe can either . We can take any value and wrap it in this context by calling the value a “success”. We do this with the constructor. We represent failure by . fail or succeed with a value Just Nothing We combine computations in this context by examining the result of the first computation. If it , we takes its value, and pass it to the . If it failed, then we have no value to pass to the next step. So the . Let’s look at how we can use the bind operator to combine our operations: succeeded second computation total computation is a failure runMaybeFuncs :: String -> Maybe [Int]runMaybeFuncs input = maybeFunc1 input >>= maybeFunc2 >>= maybeFunc3 This looks much cleaner! Let’s see why the types work out. The result of is simply . Then the bind operator allows us to take this value and combine it with , whose type is . The these to a . Then we pass this similarly through the bind operator to , resulting in our final type, . maybeFunc1 input Maybe Int Maybe Int maybeFunc2 Int -> Maybe Float bind operator resolves Maybe Float maybeFunc3 Maybe [Int] Your functions will not always combine so cleanly though. This is where notation comes into play. We can rewrite the above as: do runMaybeFuncs :: String -> Maybe [Int]runMaybeFuncs input = do i <- maybeFunc1 input f <- maybeFunc2 f maybeFunc3 f The operator is special. It effectively the value on the right-hand side from the monad. This means the value has type , the result of is . The bind operation happens under the hood, and if the function returns , then the entire function will return . <- unwraps i Int even though maybeFunc1 Maybe Int Nothing runMaybeFuncs Nothing At first glance, this looks more complicated than the bind example. However, it gives us a lot more flexibility. Consider if we wanted to add 2 to the integer before calling . This is to deal with in notation, but more difficult when simply using binds: maybeFunc2 easy do runMaybeFuncs :: String -> Maybe [Int]runMaybeFuncs input = do i <- maybeFunc1 input f <- maybeFunc2 (i + 2) maybeFunc3 f -- Not so nicerunMaybeFuncsBind :: String -> Maybe [Int]runMaybeFuncsBind input = maybeFunc1 input >>= (\i -> maybeFunc2 (i + 2)) >>= maybeFunc3 The gains are even more obvious if we want to use in a function call. Using binds, we would have to in anonymous functions. One note about do notation: we never use to unwrap the final operation in a do-block. Our call to has the type . This is our final type (not ) so we do not unwrap. multiple previous results continually accumulate arguments <- maybeFunc3 Maybe [Int] [Int] The Either Monad Now, let’s examine the monad, which is quite similar to the monad. Here’s the definition: Either Maybe instance Monad (Either a) where return r = Right r (Left l) >>= _ = Left l (Right r) >>= f = f r Whereas the either succeeds with a value or fails, the monad to failures. Just like , it wraps values in its context by calling them successful. The monadic behavior also combines operations by short-circuiting on the first failure. Let’s see how we can use this to make our code from above more clear. Maybe Either attaches information Maybe maybeFunc1 :: String -> Either String IntmaybeFunc1 “” = Left “String cannot be empty!”maybeFunc1 str = Right $ length str maybeFunc2 :: Int -> Either String FloatmaybeFunc2 i = if i `mod` 2 == 0 then Left “Length cannot be even!” else Right ((fromIntegral i) * 3.14159) maybeFunc3 :: Float -> Either String [Int]maybeFunc3 f = if f > 15.0 then Left “Float is too large!” else $ Right [floor f, ceil f] runMaybeFuncs :: String -> Either String [Int]runMaybeFuncs input = do i <- maybeFunc1 input f <- maybeFunc2 i maybeFunc3 f Before, every failure just gave us a value: Nothing >> runMaybeFuncs ""Nothing>> runMaybeFuncs "Hi"Nothing>> runMaybeFuncs "Hithere"Nothing>> runMaybeFuncs "Hit"Just [9,10] Now when we run our code, we can look at the resulting error string, and this will tell us . which function actually failed >> runMaybeFuncs ""Left "String cannot be empty!">> runMaybeFuncs "Hi"Left "Length cannot be even!">> runMaybeFuncs "Hithere"Left "Float is too large!">> runMaybeFuncs "Hit"Right [9,10] Notice we parameterize the monad by the error type. If we have: Either maybeFunc2 :: Either CustomError Float… This function is in a different monad now. It won’t be quite as simple to combine this with our other functions. If you’re curious how we might do this, check out this . answer on quora The IO Monad The is perhaps the in Haskell. It is also one of the hardest monads to understand starting out. Its actual implementation is a bit too intricate to discuss when first learning monads. So we’ll learn by example. IO Monad most important monad The IO monad wraps computations in the following context: “This computation can read information from or write information to the terminal, file system, operating system, and/or network”. If you want to get , a message to the user, read information from a , or make a , you’ll need to do so within the IO Monad. These are “side effects”. We cannot perform them from “pure” Haskell code. user input print file network call The most important job of pretty much any computer program is to in some way. For this reason, the root of all executable Haskell code is a function called , with the type . So every program starts in the IO monad. From here you can get any input you need, call into relatively “pure” code with the inputs, and then output the result in some way. The reverse does not work. You cannot call into IO code from pure code, the same way you can call into a function from pure code. interact with the outside world main IO () Maybe Let’s look at a simple program showing a few of the basic IO functions. We’ll use do-notation to illustrate the similarity to the other monads we’ve discussed. We list the types of each IO function for clarity. main :: IO ()main = do -- getLine :: IO String input <- getLIne let uppercased = map Data.Char.toUpper input -- print :: String -> IO () print uppercased So we see once again each line of our program has type . (A statement ). Just as we could unwrap in the maybe example to get an instead of a , we can use to of as a . We can then manipulate this value using string functions, and pass the result to the function. IO a let can occur in any monad i Int Maybe Int <- unwrap the result getLine String print This is a simple echo program. It reads a line from the terminal, and then prints the line back out in all caps. Hopefully it gives you a basic understanding of how IO works. We’ll get into more details in the next couple articles. Summary A monad wraps computations in a . It defines functions for in its context, and in the context. is a monad. We describe its context by saying its computations can . is similar to Maybe, except it can add error information to failures. The is hugely important, encapsulating the context of operations reading from and writing to the terminal, network, and file system. The easiest way to learn monadic code is to use . In this notation, every line has a right-side value of the monad. You can then on the left side using the operator. particular context wrapping values combining operations Maybe succeed or fail Either IO monad do notation unwrap the value <- If you want to get learning about monads, be sure to head over to the ! We have a new piece this week on the , and demonstrate how they encapsulate different kinds of side effects then we might get from the IO monad. blog Reader and Writer monads Hopefully this article has started you off on (finally) understanding monads. If you haven’t written any Haskell code yet and want to get started so you can test your knowledge of monads, be sure to check out our free for getting started with Haskell! checklist Not quite ready for monads but want to try some different Haskell skills? Check out our . It includes 2 chapters of material on recursion and higher order functions, as well as 10 practice problems with a test harness. recursion workbook is how hackers start their afternoons. We’re a part of the family. We are now and happy to opportunities. Hacker Noon @AMI accepting submissions discuss advertising & sponsorship To learn more, , , or simply, read our about page like/message us on Facebook tweet/DM @HackerNoon. If you enjoyed this story, we recommend reading our and . Until next time, don’t take the realities of the world for granted! latest tech stories trending tech stories