Search code examples
haskellmonadsindentationoperator-precedencebackticks

Operator on do indentation


hindent changed my code to:

do download i inputFile
   onException
     (callProcess (List.head args) (List.tail args))
     (removeFileIfExists name)
   `finally` removeFileIfExists inputFile

I can't determine if the finally applies to the rest of the do block, or just the state beginning onException. According to this,

If you see something unexpected in a list, like where, insert a closing brace before instead of a semicolon.

I'm unsure if that rule is applying here.

Does the `finally` apply to the rest of the do, or just the last statement and why?


Solution

  • We can find out using GHCi: writing

    Prelude> let f = (>>)
    Prelude> :{
    Prelude| do print 5
    Prelude|    print 4
    Prelude|    `f` print 3
    Prelude|    print 2
    Prelude| :}
    

    causes the following type error (not a parse error!)

    <interactive>:12:8: error:
        • Couldn't match expected type ‘(() -> IO ()) -> Integer -> IO b’
                      with actual type ‘IO ()’
        • The function ‘print’ is applied to three arguments,
          but its type ‘Integer -> IO ()’ has only one
          In the second argument of ‘f’, namely ‘print 3 print 2’
          In the expression:
            do { print 5;
                 print 4 }
            `f` print 3 print 2
    

    Looking at the list lines, we discover how GHCi parsed the code, which is printed with explicit braces-and-semicolons.

    There, we see that the `f` part closed the do block! This makes the whole do block to be the first argument to f. Further, the next lines, no longer being in a block, now form a single expression print 4 print 2 which is used as the second argument for f. This triggers a type error since it calls print with three arguments.

    Indeed, the brace } was inserted before `f` because of the rule mentioned by the OP: when something does not parse in a block, we add } and continue.

    Summarizing, if `f` is indented more, the block is parsed as

    do print 5
       print 4 `f` print 3
       print 2
    

    If `f` is indented as the previous line, or less, the block is parsed as

    (do { print 5
        ; print 4 }) `f` print 3 print 2
    

    I would suggest to avoid indenting `f` exactly as the previous line: it is better to indent it less, so that the parsing becomes obvious even to a human reader.