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 m
s 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.
do
-blocks are not statements, they are values. What do you think this is supposed to do? Like I said in your last question, please study monads before using them! – Populousdo { x ; y ; z }
explicit syntax. – Rhizo[String] -> [[String]]
and then just usefmap
to lift it into theIO
monad. – Phlebitiswords'
) outside the do block (i.e. in a guard). you will have to restructure your code. – Rhizo