> Write stupid code
Also really agree with this. As engineers our job is not just to write something that works, it's to write something that can also be read and understood by others. This is why simple solutions to problems are praised over complex solutions.
On the other hand, I think that immutable data is rarely difficult to deal with, and saves my stupid self from all kinds of bugs that I'd probably write.
A program that is 10% impure and 90% pure is not considered pure yet is still 90% easier to understand than a 100% impure program.
I'm not saying that is decidable in all cases (can't come up with a meaningful counter-example either), but usually a 90% good enough solution is better than a 100% solution that makes you go out of your way to do very simple things (like print to stdout).
Such ``compromises'' just ruin the whole carefully elaborated framework of the paradigm introducing breaches, which would be filled with workarounds. And voila, meet new java.
With partial application in the mix, functions are in an almost indeterminate state (to the programmer typing at the keyboard even if some compilers know).
In functional styles, programmers are encouraged to "just pass a function".
The f(x,y,z) gets passed around in an unknown state of f(maybe needs x, maybe needs y, maybe needs z).
This would be complicated even if it was the same function with a declining arity as it was passed around. Several languages allow named arguments which just adds to the signature complexity.
It's hard to see the benefits of the complexification.
Most pedagogical examples of the use occur in tight loops where the complexity isn't obvious. Once a function is passed outside of that tight loop I wonder at it's actually utility vs the negative effects.
I will give you my two cents on its benefits though and let me know if you agree or disagree. I would compare it to taking a step up on the ladder of abstraction. Computers are powerful because they are programmable. If we take the example of a value like an integer, in a computer we can make it anything we want (0, 1, 2, 3, 4 etc). Functions are how we program a value. We say for example
speed(distance, time) { return distance / time; }
The value of the speed depends on these two variables (distance and time) which are programmable.
Partial function application is just a continuation of this idea. It's obvious that `values` are programmable, but why can't functions be programmable as well? After all, we gained a lot of power from letting values be programmable maybe the same will be true for functions? Using the previous example lets say we want a function to calculate the speed of runners in the 100m dash. We could write a new function for this
speedOf100YardDash(time) { return 100 / time; }
but we already have logic for calculating the speed and we don't want to reuse it. So the idea of having a `programmable` speed function starts to look a little better.
speedOf100YardDash = speed(where `distance` = 100)
Obviously, this plays into your criticism of 'pedagogical examples' being simple, but I think the idea is even more valuable with more complicated code. Why? Because with simple examples it's easy it duplicate code without much consequence. The value of speed and division are not changing anytime soon. If we have more complex functions, however, this is where we absolutely want to avoid code duplication. Because if we write a complex function multiple times it's more likely that one of those implementations will be written differently or eventually diverge from the other one.
What do you think?
* function under test takes in a data as its last parameter
* partially applied version of this function is used in a test
* testing function constructs fake data
* testing function calls the partially applied version
It's also nice for code reuse in libraries, but I'm generally less inclined to use it unless I know that the thing I'm working on has dead-simple requirements and won't change much.
* Annotating your parameter types is a life saver with large, complicated codebases. No type annotations is cute and convenient in the short-term, but horrible when you need to understand some code that sorta doesn't work 6 months later.
* Write stupid code. I recently had to muck about in some old code that called into a function that called into a function created from another function that produced a 5-tuple of partially evaluated functions. This function called into another partially evaluated function, and then another, until it crossed an assembly boundary. Clever code like that is impossible to understand if you didn't write it yourself. Luckily, languages like F# and OCAML make it really easy to write stupid code.
* Write code that makes tooling "light up". This is usually also stupid code. There are ways you can write F# code that make any kind of IDE tooling useless. It's not fun to deal with.
* Purity is overrated. Immutability isn't.