do notation

Contents
Bind with nicer syntax
The three statements
do in Maybe
Nested do blocks

Bind with nicer syntax

A do block is bind written vertically. Everything after <- is a monadic value, and the rest of the block becomes a function of the unwrapped result:

-- with bind find "a" prices >>= \pa -> find "b" prices >>= \pb -> pure (pa + pb) -- the same code do pa <- find "a" prices pb <- find "b" prices pure (pa + pb)

Nothing changes at runtime. The notation exists because a long chain of lambdas reads inside out, and the block reads top to bottom.

The three statements

A do block has three kinds of line, and only the last one may be a plain expression:

do x <- action -- bind the result let y = x * 2 -- pure binding, no <- action2 -- run, ignore the result pure y -- last line is the result

A let line inside do needs no in, because the rest of the block is the scope. A line without <- runs for its effect and discards the value.

do in Maybe

The same notation works for every monad. Here it is with Maybe, where any Nothing skips the rest:

safeDiv :: Int -> Int -> Maybe Int safeDiv _ 0 = Nothing safeDiv a b = Just (a `div` b) average2 a b = do x <- safeDiv a b y <- safeDiv x 2 pure y

If the first division fails, the block returns Nothing without evaluating the second. This short-circuit is a property of the instance, not of the syntax.

Nested do blocks

A block inside a block is fine and often clearer than one long sequence. Keep the indentation consistent or the layout rule will fight you:

report = do a <- find "a" prices let doubled = a * 2 b <- do v <- find "b" prices pure (v + doubled) pure b

When a block grows past ten lines, split it into named functions that return monadic values. The do stays at the top level, and the pieces become testable on their own.

Next: IO and purity

Article author: Arthur Isaev

Related articles
Haskell
Haskell course: zero to hero in 50 lessons
What is Haskell and why it matters
Install the toolchain with GHCup
GHCi: the interactive shell
Your first program
Values with let and where
Types and type signatures
Functions and application
Currying and partial application
Operators, sections, and fixity
if, guards, and pattern guards
case expressions
Pattern matching
Tuples
Lists
Ranges and enumeration
List comprehensions
Recursion replaces loops
map, filter, and foldr
Lambdas and composition
Type classes
Parametric polymorphism
Maybe and null safety
Either and recoverable errors
data: product types
data: sum types
Records
newtype and type synonyms
Recursive data: binary trees
Functor
Applicative
Monad
do notation
IO and purity
Files and IO
Modules and imports
Cabal projects
Dependencies
Testing with HUnit
Property testing with QuickCheck
Laziness and infinite lists
Strictness and space leaks
Folds
Monoids
Traversable
Monad transformers
Errors and exceptions
Concurrency
Parsing with Megaparsec
Capstone: a todo CLI
Hero roadmap: the whole course on one page

Search this site

Channel @aofeed Chat @aofeedchat

Contacts and cooperation:
I recommend our hosting beget.com
Write to info@aredel.com if you:
1. Want to write an article for our site or translate an article into your native language.
2. Want to place thematically relevant ads on the site.
3. Ads on my site pass maximum censorship. If you see an ad block unsuitable for school-age children, shocking or misleading - please contact us by e-mail
4. Found a mistake, inaccuracy, bug, etc. on the site. ... .......
5. Articles can be shared on social media by clicking a network icon: