Search code examples
haskellcode-translationdo-notationcode-transformation

How are expressions allowed inside Haskell do blocks


In the following code, line 4, I have an expression sandwiched between two IO actions in a do block:

  1 doubleX :: (Show x, Num x) => x -> IO ()                                                                                                                                                                                          
  2 doubleX x = do                                                                                                                                                                                                                    
  3   putStrLn ("I will now double " ++ (show x))                                                                                                                                                                                     
  4   let double = x * 2                                                                                                                                                                                                              
  5   putStrLn ("The result is " ++ (show double))

I understand do notation as chaining monadic operations together using >>= or >>. But how does that work when you have an expression in between? You couldn't just glue lines 3-5 together using >>.


Solution

  • I'm going to crib from my very similar answer here (though probably not a duplicate since that question doesn't explicitly deal with let).

    The Report gives a full translation from do syntax into kernel Haskell; the parts relevant to your question are:

    do {e}                = e
    do {e;stmts}          = e >> do {stmts}
    do {let decls; stmts} = let decls in do {stmts}
    

    So your code desugars like this:

    doubleX x = do                                                                                                                                                                                                                    
      putStrLn ("I will now double " ++ (show x))                                                                                                                                                                                     
      let double = x * 2                                                                                                                                                                                                              
      putStrLn ("The result is " ++ (show double))
    
    ==> do {e;stmts} rule
    
    doubleX x =
      putStrLn ("I will now double " ++ (show x)) >> do
      let double = x * 2                                                                                                                                                                                                              
      putStrLn ("The result is " ++ (show double))
    
    ==> do {let decls; stmts} rule
    
    doubleX x =
      putStrLn ("I will now double " ++ (show x)) >>
      let double = x * 2 in do
      putStrLn ("The result is " ++ (show double))
    
    ==> do {e} rule
    
    doubleX x =
      putStrLn ("I will now double " ++ (show x)) >>
      let double = x * 2 in
      putStrLn ("The result is " ++ (show double))