Audience
I assume familiarity with the Haskell programming language or some other language that supports calling a function with less arguments than the function's signature requires. This is called "partial application of a function".
Consider the following function-call expression in Haskell:
(alpha beta gamma)
What can be deduced?
alpha is a function (either imported or in a variable).In most languages (that disallow implicit partial application) you could additionally deduce:
alpha takes exactly 2 argumentsalpha.Neither of those two properties are necessarily true in Haskell.
In this case, the expression behaves as you’d expect in any other language. In particular:
alpha takes exactly 2 argumentsalpha.In this case, the expression is a partial application of alpha, and would be perhaps better read as:
(\ ... -> alpha beta gamma ...)
Or, in a more C-like notation:
function (...) { return alpha(beta, gamma, ...); }
Thus:
alpha takes exactly 2+K arguments, for some unknown K.Let’s say alpha takes 1 argument. This would be intuitively read as:
((alpha beta) gamma)
After this reformulation you need to recursively examine the new expression.
In this example, we can deduce:
(alpha beta) returns an anonymous function of (at least) 1 argument.gamma, and the original expression’s return type matches the return type of the anonymous function.gamma, and yet another anonymous function (that takes K parameters) is returned as the result of the original expression.What a mess. If alpha does not in fact take two arguments then I have to exert non-trivial effort to derive the type of the expression - or even what the expression semantics are.
There are two ways I can see to simplify these weird cases:
Then we would see syntax like:
-- alpha takes 2 arguments -- return type matches that of alpha (alpha beta gamma) -- alpha takes 2+K arguments; -- return type is a partially applied K-argument function (alpha beta gamma ...) -- alpha takes 1 argument and returns a 1-argument function; -- expression's return type matches that of the 1-argument function ((alpha beta) gamma) -- alpha takes 1 argument and returns a (1+K)-argument function; -- expression's return type is a partially applied K-argument function ((alpha beta) gamma ...)
Notice that each conceptually different case now has a unique syntactic representation - it’s no longer just (alpha beta gamma) for all cases.
For example parentheses could be used instead of a space to signify function calls.
Using a grouping operator syntactically prohibits relying on the left-associativity of space for partial function application, since a grouping operator doesn’t have associativity.
If you combined this suggestion with the explicit syntax extension above, you would get syntax like:
-- alpha takes 2 arguments -- return type matches that of alpha alpha(beta, gamma) -- alpha takes 2+K arguments; -- return type is a partially applied K-argument function alpha(beta, gamma, ...) -- alpha takes 1 argument and returns a 1-argument function; -- expression's return type matches that of the 1-argument function alpha(beta)(gamma) -- alpha takes 1 argument and returns a (1+K)-argument function; -- expression's return type is a partially applied K-argument function alpha(beta)(gamma, ...)
I originally got bitten by this syntactic ambiguity when trying to decipher the meaning of:
(flip (/) 20)
I was not previously familar with flip and so I assumed that it took two arguments, a function (namely (/)) and a non-function (namely 20). And that it probably returned a function, since its surrounding context expected a function.
In fact flip takes only one argument and thus is more clearly written as:
((flip (/)) 20)
And inlined further to be the lower-order:
(\x -> x / 20)
And, if desired, further rewritten to be the more-compact:
(/ 20)
This last form uses partial application to fill in the first argument of /, which is obvious since / is a well-known built-in infix operator that requires an unspecified left argument.