Do notation is syntactic sugar for nested binds and lambdas: the line x <- mx (“x gets the result of mx”) followed by rest desugars to mx >>= \x -> rest; the last line must be a monadic value (often return e). It lets one name intermediate results of Kleisli arrows instead of composing them point-free with <=<.
main = do -- desugars to: getLine >>= \s1 ->
s1 <- getLine -- getLine >>= \s2 ->
s2 <- getLine -- putStrLn ("Hello " ++ s1 ++ " " ++ s2)
putStrLn ("Hello " ++ s1 ++ " " ++ s2)Sources: DaoFP §14.5 (“Do Notation”), Exercises 14.5.1–14.5.2; §14.6 (CPS via
Contin do notation); §14.9 (ApplicativeDo).
pairs as bs = do { a <- as; b <- bs; return (a, b) }in the List Monad;ap fs as = do { f <- fs; a <- as; return (f a) }for any monad (DaoFP Exercise 14.5.1).- The final
returntypically needs variables bound in outer lambdas — this depends on the monad being strong, which every Haskell functor is. ApplicativeDolets the compiler use applicative combinators where no line depends on an earlier result, enabling parallelism. Imperative coroutines (C++) mimic do notation for hard-coded monads.
Docs: plain Julia — Catlab has no dedicated API for this; related: Catlab v0.16 docs · GATlab standard library
# a tiny "do" as a macro over a bind function
macro mdo(bind, block)
lines = filter(x -> !(x isa LineNumberNode), block.args)
ex = lines[end]
for l in reverse(lines[1:end-1])
if l isa Expr && l.head == :call && l.args[1] == :<--
ex = :($bind($(l.args[3]), $(l.args[2]) -> $ex))
else
ex = :($bind($l, _ -> $ex))
end
end
esc(ex)
end
bindL(as, k) = reduce(vcat, (k(a) for a in as); init=Any[])
@mdo bindL begin
a <-- [1, 2]
b <-- ['x', 'y']
[(a, b)]
end # [(1,'x'), (1,'y'), (2,'x'), (2,'y')]-- Lean's do notation is the same sugar over bind
example : Option ℕ := do
let a ← some 1
let b ← some 2
pure (a + b)ap :: Monad m => m (a -> b) -> m a -> m b
ap fs as = do
f <- fs
a <- as
return (f a)