Just treat the do
expressions completely separately: nesting them does not change how they get desugared. For your to example, we can start with the bottom line:
reverse2lines =
do { line1 <- getLine ;
do { line2 <- getLine ;
do { putStrLn (reverse line2) ;
putStrLn (reverse line1) } } }
then the next one:
reverse2lines =
do { line1 <- getLine ;
do { line2 <- getLine ;
putStrLn (reverse line2) >> putStrLn (reverse line1) } }
which is, in fact, just like:
reverse2lines =
do { line1 <- getLine ;
do { line2 <- getLine ;
putStrLn (reverse line2)
putStrLn (reverse line1) } }
then we can turn the inner remaining do
into a lambda:
reverse2lines =
do { line1 <- getLine ;
getLine >>= \ line2
putStrLn (reverse line2) >>
putStrLn (reverse line1) }
And then, if we go backwards, we see that it's the same as just:
reverse2lines =
do { line1 <- getLine ;
line2 <- getLine ;
putStrLn (reverse line2) ;
putStrLn (reverse line1) }
So, as you can see by going through the whole example, the nested version is the same as the flat version. In fact, if you just look at the rules for desugaring do
expressions (and we've seen the important ones), you'll see that nesting them like this does not change anything.
In fact, this is pretty natural because the way do
is desugared is defined recursively: each time we desugar a single line, we recurse on all the lines following it as if they were another do
expression.