Skip to content

Haskell Monads Guide — Monad Typeclass, do Notation, and Common Monads

DodaTech Updated 2026-06-28 3 min read

In this tutorial, you will learn about Haskell Monads Guide. We cover key concepts, practical examples, and best practices to help you master this topic.

Haskell Monad typeclass extends Applicative with (>>=) (bind) for chaining effectful operations where each step can depend on the previous result -- with do notation providing readable imperative-style syntax for monadic code.

Monad Typeclass

class Applicative m => Monad m where
    (>>=)  :: m a -> (a -> m b) -> m b
    return :: a -> m a  -- same as pure

-- Laws:
-- return x >>= f = f x
-- m >>= return = m
-- (m >>= f) >>= g = m >>= (\x -> f x >>= g)

Maybe Monad

-- Safe division chain
safeDiv :: Int -> Int -> Maybe Int
safeDiv _ 0 = Nothing
safeDiv x y = Just (x `div` y)

compute = do
    a <- safeDiv 10 2
    b <- safeDiv a 3
    c <- safeDiv b 0  -- Nothing here
    return c

-- Using (>>=)
compute' = safeDiv 10 2 >>= \a ->
           safeDiv a 3 >>= \b ->
           safeDiv b 0

List Monad

-- Nondeterministic computation
pairs = do
    x <- [1,2,3]
    y <- [4,5]
    return (x, y)
-- [(1,4),(1,5),(2,4),(2,5),(3,4),(3,5)]

-- With guard
evenPairs = do
    x <- [1..10]
    guard (even x)
    y <- [1..10]
    guard (y > x)
    return (x, y)

Either Monad

-- Error handling with Either
type Error = String

validateAge :: Int -> Either Error Int
validateAge n
    | n < 0     = Left "Negative age"
    | n > 150   = Left "Age too high"
    | otherwise = Right n

validateName :: String -> Either Error String
validateName "" = Left "Empty name"
validateName n  = Right n

process = do
    name <- validateName "Alice"
    age  <- validateAge 30
    return (name, age)
    -- Right ("Alice", 30)

IO Monad

main :: IO ()
main = do
    putStrLn "Enter your name:"
    name <- getLine
    putStrLn $ "Hello, " ++ name ++ "!"
    content <- readFile "data.txt"
    writeFile "output.txt" content

Common Mistakes

1. Forgetting return

In do notation, the last line must be an expression of the monadic type. Use return to wrap pure values: return (x + y).

2. Using return unnecessarily in pure code

return has no special meaning in Haskell -- it just wraps a value. Don't use it in non-monadic code.

3. Confusing (>>=) with (>>)

(>>) ignores the result: action1 >> action2 runs action1, then action2. Use when you only need the side effects.

Practice Questions

1. What does (>>=) do? Bind: extracts a value from a monadic context and passes it to a function that returns a new monadic value.

2. What is do notation syntactic sugar for? Using (>>=) and (>>). do { x <- a; b } becomes a >>= \x -> b.

3. What is the difference between Maybe and Either as Monads? Maybe short-circuits on Nothing. Either short-circuits on Left. Either carries an error value; Maybe carries nothing.

FAQ

{{< faq question="What are the Monad laws?" >}} Left identity: return x >>= f = f x. Right identity: m >>= return = m. Associativity: (m >>= f) >>= g = m >>= (\x -> f x >>= g). {{< /faq >}}

{{< faq question="What is the Writer monad?" >}} Writer accumulates a log or output alongside a value: Writer w a where w is a monoid for accumulation. {{< /faq >}}

{{< faq question="What is the State monad?" >}} State threads a state value through computations: do { modify (+1); x <- get; put (x * 2) }. {{< /faq >}}

What's Next

Now learn about IO in Haskell.

Topic Description Link
IO Input/output operations {{< ref "12-io" >}}
Records Data record syntax {{< ref "13-records" >}}

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro