Operator on do indentation
Asked Answered
P

2

3

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?

Paulie answered 26/2, 2018 at 9:3 Comment(1)
Finally takes (like all operators) lower precendence than functions, so it is finally (onException (callProcess ...) (removeFileIfExists name)) (removeFileIfExists inputFile). finally is thus the main function.Turoff
E
4

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.

Evangelical answered 26/2, 2018 at 15:5 Comment(0)
K
0

The following does not actually adress the code you posted, but the question you seemed to be asking. It adresses the following code instead:

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

chi's answer adresses the actually posted code, with `finally` not indented any further than download and onException. I'd remark that this is bad style: when writing that, be sure to indent it less, i.e.

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

As Willem Van Onsem commented, function applications always take precedence before infix applications. This is true for both “true infix operators” like + or >>=, and for backtick-infixes... if fact there is no baked in distinction between these sorts of operators as far as precedence is concerned: for all infixes the precedence is determined by a fixity declaration, such as

infixl 6 +
infix 4 `elem`

The fixity is a number in the range 0..9, and fixity can be either left- right- or non-associative. If no fixity is declared (as with most named functions, whereas for symbol-infixes it's highly recommended to give a fixity declaration), the default infixl 9 is used, i.e. basically the highest legal fixity you could manually assign.

OTOH, function application is always infixl 10 as it were, i.e. it binds tigher than any infix, no matter how it's declared. So, your example is parsed as

do (download i inputFile)
   (  (onException
        (callProcess (List.head args) (List.tail args))
        (removeFileIfExists name))
    `finally`
      (removeFileIfExists inputFile) )
Kirima answered 26/2, 2018 at 11:31 Comment(4)
If I decrement `` finally ``, it's now against the entire do block?Paulie
You mean, if you decrease its indentation so it starts left of download and of onException? Yes, that would make it parse as (do {...}) `finally` (...).Kirima
This doesn't really address the question, which was about how an operator interacts with the do constructAder
Quite right, I got side-tracked. Leaving this answer here nevertheless, with a header on top refering to the other one.Kirima

© 2022 - 2024 — McMap. All rights reserved.