Reputation: 20653
In this question Will's answer states that the following code (let's call it code A
):
reverse2lines :: IO ()
reverse2lines =
do line1 <- getLine
line2 <- getLine
putStrLn (reverse line2)
putStrLn (reverse line1)
can be transformed into the following (let's call it code B
) :
reverse2lines =
do { line1 <- getLine ;
do { line2 <- getLine ;
do { putStrLn (reverse line2) ;
do { putStrLn (reverse line1) } } } }
I am confused. I understand, for example, that
addOneInt :: IO ()
addOneInt = do line <- getLine
putStrLn (show (1 + read line :: Int))
can be transformed into:
addOneInt' :: IO ()
addOneInt' = getLine >>= \line ->
putStrLn (show ( 1 + read line :: Int))
But I don't understand how the nested do
transformation works. In other words, I don't understand how one arrives from code A
to code B
? What are the rules governing this transformation ?
Where are these rules described / explained / specified ?
Could someone please explain what is going on here with this (codeA
to codeB
) transformation ?
For example, what does do { command1; do {command2 } }
mean ? How should I interpret that ? What is its definition ?
In other words,
what is the difference between
do {command1;command2}
and
do {command1; do {command2}}
?
Furthermore, what is the difference between
do {command1; do{command2};command3}
and
do {command1;do {command2; do {command3}}}
?
Thanks for reading.
Upvotes: 4
Views: 3424
Reputation: 68172
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.
Upvotes: 5