Home > Net >  Precedence of function application
Precedence of function application

Time:02-11

In order to illustrate function application has the highest precedence in Haskell the following example was provided (by schoolofhaskell):

sq b = b * b
main = print $
-- show
    sq 3 1 
-- /show

The result here is 10.

What puzzles me is that the argument constitutes a function application too. Consider the operator being a short-cut of a function. So when the argument is taken, I would expect that its function application now takes precedence over the original one.

Written that way it delivers the expected result:

sq b = b * b
main = print $
    sq (( ) 3 1 )

Any explanation?

CodePudding user response:

What puzzles me is that the argument constitutes a function application too. Consider the operator " " being a short-cut of a function.

I think this is the heart of the misunderstanding (actually, very good understanding!) involved here. It is true that 3 1 is an expression denoting the application of the ( ) function to 3 and 1, you have understood that correctly. However, Haskell has two kinds of function application syntax, prefix and infix. So the more precise version of "function application has the highest precedence" would be something like "syntactically prefix function application has higher precedence than any syntactically infix function application".

You can also convert back and forth between the two forms. Each function has a "natural" position: names with only symbols are naturally syntactically infix and names with letters and numbers and the like are naturally syntactically prefix. You can turn a naturally prefix function to infix with backticks, and a naturally infix function to prefix with parentheses. So, if we define

plus = ( )

then all of the following really mean the same thing:

3   1
3 `plus` 1
( ) 3 1
plus 3 1

Returning to your example:

sq 3 1

Because sq is naturally prefix, and is naturally infix, the sq application takes precedence.

CodePudding user response:

So when the argument is taken, would expect that its function application now takes precedence over the original one.

The Haskell grammar [Haskell report] specifies:

exp10
  → … 
  | …  
  | fexp
fexp
  →  [fexp] aexp   (function application)

This means that function application syntax has precedence 10 (the superscript on the exp10 part).

This means that, when your expression is parsed, the empty space in sq 3 takes precedence over the in 3 1, and thus sq 3 1 is interpreted as (sq 3) 1 which semantically means that it squares 3 first, and then adds 1 to the result, and will thus produce 10.

If you write it as sq (3 1) or in canonical form as sq (( ) 3 1) it will first sum up 3 and 1 and then determine the square which will produce 16.

CodePudding user response:

The addition operator is syntactically different from a function application, and that is what determines its operator precedence.

If you rewrite your addition (3 1) as a function application (( ) 3 1), the slice ( ) follows special slicing rules inside its own parentheses, but outside the slice it's just another parenthesized expression.


Note that your "expected result" is not really parallel to your original example:

sq 3   1      -- original example, parsed `(sq 3)   1`

sq (( ) 3 1)  -- "expected result", has added parentheses to force your parse

sq (3   1)    -- this is the operator version of "expected result"

In Haskell, the parentheses are not part of function application -- they are used solely for grouping!

That is to say: like ( ) is just another parenthesized expression, so is (3 1)

CodePudding user response:

I think your confusion is just the result of slightly imprecise language (on the part of both the OP and the School of Haskell page linked).

When we say things like "function application has higher precedence than any operator", the term "function application" there is not actually a phrase meaning "applying a function". It's a name for the specific syntactic form func arg (where you just write two terms next to each other in order to apply the first one to the second). We are trying to draw a distinction between "normal" prefix syntax for applying a function and the infix operator syntax for applying a function. So with this specific usage, sq 3 is "function application" and 3 1 is not. But this isn't claiming that sq is a function and is not!

Whereas in many other contexts "function application" does just mean "applying a function"; there it isn't a single term, but just the ordinary meaning of the words "function" and "application" that happen to be used together. In that sense, both sq 3 and 3 1 are examples of "function application".

These two senses of the term arise because there are two different contexts we use when thinking about the code1: logical and syntactic. Consider if we define:

add = ( )

In the "logical" view where we think about the idealised mathematical objects represented by our code, both add and ( ) are simply functions (the same function in fact). They are even exactly the same object (we defined one by saying it was equal to the other). This underlying mathematical function exists independently of any name, and has exactly the same properties no matter how we choose to refer to it. In particular, the function can be applied (since that is basically the sole defining feature of a function).

But at the syntactic level, the language simply has different rules about how you can use the names add and (regardless of what underlying objects those names refer to). One of these names is an operator, and the other is not. We have special syntactic rules for the how you need to write the application of an operator, which differs from how you need to write the application of any non-operator term (including but not limited to non-operator names like sq2). So when talking about syntax we need to be able to draw a distinction between these two cases. But it's important to remember that this is a distinction about names and syntax, and has nothing to do with the underlying functions being referred to (proved by the fact that the exact same function can have many names in different parts of the program, some of them operators and some of them not).

There isn't really an agreed upon common term for "any term that isn't an operator"; if there was we would probably say "non-operator application has higher precedence than any operator", since that would be clearer. But for better or worse the "application by simple adjacency" syntactic feature is frequently referred to as "function application", even though that term could also mean other things in other contexts3.

So (un?)fortunately there isn't really anything deeper going on here than the phrase "function application" meaning different things in different contexts.


1 Okay, there are way more than two contexts we might use to think about our code. There are two relevant to the point I'm making.

2 For an example of other non-operator terms that can be applied we can also have arbitrary expressions in parentheses. For example (compare on fst) (1, ()) is the non-operator application of (compare `on` fst) to (1, ()); the expression being applied is itself the operator-application of on to compare and fst.

3 For yet another usage, $ is often deemed to be the "function application operator"; this is perhaps ironic when considered alongside usages that are trying to use the phrase "function application" specifically to exclude operators!

  • Related