Do notation is used to write expressions of the general form
ex :: Monad m => m t
let ex = do
{ x <- foo -- foo :: Monad m => m a, x :: a
; y <- bar x -- bar x :: Monad m => m b, y :: b
; z <- baz x y -- baz x y :: Monad m => m c, z :: c
; quux x y z -- quux x y z :: Monad m => m t
}
Notice all the ms are the same, and a, b, c, ... can be different, though the t in the last do sub-expression's type and the overall do expression's type is the same.
The do notation variables are said to be "bound" by the <- construct. They come into scope at their introduction (to the left of <-) and remain in scope for all the subsequent do sub-expressions.
One built-in monadic expression available for any monad is return :: Monad m => a -> m a. Thus x <- return v binds x to v, so that x will be available in the subsequent sub-expressions, and will have the value of v.
All the do variables are confined to that do block, and can not be used outside it. Each variable's scope is all the code in the same do block, below / after the variable's binding.
This also means that <-'s is a non-recursive binding, since the variable can't go on its right hand side as well as the left: it will be two different variables with the same name, in that case, and the variable on the right will have to have been established somewhere above that point.
There are a few general patterns here:
do { _ <- p ; _ <- q ; r } === do { p ; q ; r }
do { x <- p ; return x } === do { p } === p
do { x <- return v ; foo x } === do { foo v } === foo v
do { p ; q ; r } === do { p ; do { q ; r } }
=== do { do { p ; q } ; r }
do { x <- p ; === do { x <- p ;
y <- q x ; z <- do { y <- q x ;
return (foo x y) } return (foo x y) } ;
return z }
All the Monad m => m a expressions are just that, expressions, and so can in particular be an if - then - else expressions with both the consequent and the alternative branches being of the same monadic type (which is often confusing for beginners):
do { x <- p ;
y <- if (pred x) then (foo x) else (bar x) ;
return (baz x y) }
update: One of the main points of the monad is its total separation of effects from the pure calculations. Once in a monad, you can't "get out". Monadic computations can use pure calculations, but not vice versa.