Only Complete Applications
![]() |
Fig 1. PFA RFC |
It was less effort to write a debugger for PHP than it was to make partial function application work!
The debugger, a few of us wrote in a few days. Partial application took up many weeks of my life, including most of the night time.
It actually ran me into the ground.
I'm not a very good communicator. I like blogs because I can perform an editorial process, re-arrange my thoughts and move toward the perfect words.
But, other humans, in general, baffle me. I can't tell how other people think, and what they know, so I can't understand what they are saying or what they need me to say a lot of the time. When you ask me a question, even if I definitely know the answer, I'll spend some time paralyzed by anxiety, to some degree caused by all my previous failures to communicate with humans properly.
A lot of questions were being asked of me the whole time, and this took a toll ... simply put, I spent quite a lot of this time anxious, exhausted, and sad.
In addition, I became physically sick toward the end with suspected Covid. But, I tested negative, had a 24 (maybe 20) hour break and more or less carried on at the same pace.
It is a huge understatement to say I put a lot of effort into this thing ...
In that time, we had at least two different full implementations of the idea.
The first implementation had only one placeholder, the complexity of the implementation at this point was quite low. However, it resulted in semantics that apparently didn't make sense to anyone. The main gripe being that the placeholders semantics depended on its position in a list of arguments.
Any implementation of this is so involved with details of the engine that inevitable bike shedding ensued.
Rather than looking around at other languages, we decided we needed multiple placeholder symbols, support for partial application of the new operator, out of order application by supporting named arguments, and even named placeholders - essentially changing the feature into something other than partial application - that's actually very definitely function (API) redefinition.
Between the first and last implementation, I attempted to move toward the semantics people wanted, while retaining as much simplicity in the implementation as possible.
At one point, I had an implementation that supported all of the crazy things people were bike shedding about, including re-ordering named parameters (necessary for named placeholder support).
What became clear in this time is that we needed to define the semantics in such a way that limits complexity.
We dropped named placeholders, and settled on two symbols with easy to understand semantics. While we're left with semantics and rules that you can write in a few lines, it only limits complexity, you are still left with something complicated.
We dropped named placeholders, and settled on two symbols with easy to understand semantics. While we're left with semantics and rules that you can write in a few lines, it only limits complexity, you are still left with something complicated.
Then we get to the last implementation, which I stayed awake for more than 30 hours, while sick, to write.
I made some glaring (to some people) omissions, but overall we had a solid implementation with easy to understand semantics and soon after the vote started.
Why did this fail ?
If you asked me why it failed, I would have to say bike shedding is somewhat to blame.
Read the next sentence with the logical bit of your brain:
People who don't know how to implement something are not well equipped to decide how that something should work.
This seems to be an obvious truth, but may come across as elitist ... I don't actually care.
Elitism is good - You want an elite doctor to perform your eye surgery, you want elite scientists doing the research that will save the world. I'm all for elitism ...
I'm not saying we shouldn't listen to feedback - I did remember, even when it was detrimental to the implementation, not to mention detrimental to my physical and mental health.
I know that the people in the bike shed, making suggestions, making complaints, in some cases explicitly bike shedding ("I know this is bike shedding, but"), they think they are helping the conversation along by talking about things they understand, while ignoring all the stuff they don't understand outside the bike shed.
I'm willing to admit that sometimes they do move the conversation along, but think it's by accident; The conversation would have moved along anyway, possibly faster if they hadn't intervened.
Here, bike shedding resulted in a lot of wasted time, that's a fact.
The other reason is complexity. There are two distinct kinds of complexity here:
- language complexity - what do people that are writing PHP have to know in order to understand code containing partial application ?
- implementation complexity - what do people that are maintaining, debugging, or developing the engine have to know ?
When it comes to language complexity, this is mostly determined by semantics. Once we landed on semantics we can explain in a few sentences, we've reduced that as much as we can.
When it comes to internal, implementation complexity ...
Why is this complicated ?
We can all knock up a class in 10 minutes that performs something that looks a bit like partial application, we can do that in userland.
What it won't be, is partial application: You cannot do the things we do internally from userland; You don't have the ability to rebuild prototypes (in any sensible way), manipulate the stack, interact with the GC in certain ways, and a list of a million other things.
The fact is that any proper implementation of partial application is inherently complicated.
Some of that complexity is due to the semantics you choose for placeholder symbols (even when reduced as much as possible), and some is due to the interactions between the engine and this new kind of object, created by interrupting a call where the engine does not expect this kind of interruption.
If we're going to have a proper implementation of partial application, that retains type information, is efficient (cumulative, as partial application is meant to be), and has semantics that are useful and easy to understand, then the implementation carries with it complexity that cannot be reduced.
Was the right decision made ?
Yes.
Although other contributors to the RFC were focused on the use case of pipes, I don't really even like pipes.
My motivation for doing any of this, is that it was interesting to write. My motivation for wanting it to be actually merged is that I'm interested in the use cases that would have been found beyond those we suggested. I don't know what they look like, and guess I'll never find out.
It's highly likely those use cases, which I imagined existed, simply do not exist.
Those voters that could think of use cases, but voted no because they couldn't look past language or implementation complexity, were absolutely right to do so.
Complexity must be justified, and if it isn't, we should not add the feature.
That's all I have to say about that right now ...
Peace out phomies :)