It's also a question of whether this is exclusive to a curried definition or if such an optimization may also apply to partial application with a special operator like in the article. I think it could, but the compiler might need to do some extra work?
foldr f z = go
where
go [] = z
go (x : xs) = f x (go xs)
when called with (+) and 0 can be inlined togo xs = case xs of
[] -> 0
(x : xs) = x + go xs
which doesn't have to create a closure to pass around the function and zero value, and can subsequently inline (+), etc.getClosest :: Set Point -> Point -> Point
You could imagine getClosest build a quadtree internally and that tree wouldn't depend on the second argument. I say slightly contrived because I would probably prefer to make the tree explicit if this was important.
Another example would be if you were wrapping a C-library but were exposing a pure interface. Say you had to create some object and lock a mutex for the first argument but the second was safe. If this was a function intended to be passed to higher-order functions then you might avoid a lot of unnecessary lock contention.
You may be able to achieve something like this with optimisations of your explicit syntax, but argument order is relevant for this. I don't immediately see how it would be achieved without compiling a function for every permutation of the arguments.
The flip side of your example is that people see a function signature like getClosest, and think it's fine to call it many times with a set and a point, and now you're building a fresh quadtree on each call. Making the staging explicit steers them away from this.
Irrespective of currying, this is a really interesting point - that the structure of an API should reflect its runtime resource requirements.
I was imagining you might achieve this optimization by inlining the function. So if you have
getClosest(points, p) = findInTree(buildTree(points), p)
And call it like myPoints = [...]
map (getClosest(myPoints, $)) myPoints
Then the compiler might unfold the definition of getClosest and give you map (\p -> findInTree(buildTree(myPoints), p)) myPoints
Where it then notices the first part does not depend on p, and rewrite this to let tree = buildTree(myPoints) in map (\p -> findInTree(tree, p)) myPoints
Again, pretty contrived example. But maybe it could work.I don't believe inlining can take you to the exact same place though. Thinking about explicit INLINE pragmas, I envision that if you were to implement your partial function application sugar you would have to decide whether the output of your sugar is marked INLINE and either way you choose would be a compromise, right? The compromise with Haskell and curried functions today is that the programmer has to consider the order of arguments, it only works in one direction but on the other hand the optimisation is very dependable.
In that case I want the signature of "this function pre-computes, then returns another function" and "this function takes two arguments" to be different, to show intent.
> achieved through compiler optimisation
Haskell is different in that its evaluation ordering allows this. But in strict evaluation languages, this is much harder, or even forbidden by language semantics.
Here's what Yaron Minsky (an OCaml guy) has to say:
> starting from scratch, I’d avoid partial application as the default way of building multi-argument functions.
https://discuss.ocaml.org/t/reason-general-function-syntax-d...