Re: [Pre-RFC Discussion] User Defined Operator Overloads (again)
| From: | Larry Garfield | Date: | Mon, 16 Sep 2024 04:10:01 +0000 |
| Subject: | Re: [Pre-RFC Discussion] User Defined Operator Overloads (again) | ||
| References: | 1 | Groups: | php.internals |
| Request: | Send a blank email to internals+get-125560@lists.php.net to get a copy of this message | ||
On Sat, Sep 14, 2024, at 4:48 PM, Jordan LeDoux wrote:
> Hello internals,
>
>
> This discussion will use my previous RFC as the starting point for
> conversation:
> https://wiki.php.net/rfc/user_defined_operator_overloads
>
>
> There has been discussion on list recently about revisiting the topic
> of operator overloads after the previous effort which I proposed was
> declined. There are a variety of reasons, I think, this is being
> discussed, both on list and off list.
>
>
> 1. As time has gone on, more people have come forward with use cases.
> Often they are use cases that have been mentioned before, but it has
> become more clear that these use cases are more common than was
> suggested previously.
>
> 2. Several voters, contributors, and participants have had more time
> (years now) to investigate and research some of the related issues,
> which naturally leads to changes in opinion or perspective.
>
> 3. PHP has considered and been receptive toward several RFCs since my
> original proposal which update the style of PHP in ways which are
> congruent with the KIND of language that has operator overloads.
>
>
> I mentioned recently that I would not participate in another operator
> overload RFC unless I felt that the views of internals had become more
> receptive to the topic, and after some discussion with several people
> off-list, I feel that it is at least worth discussing for the next
> version.
>
>
> Operator overloads has come up as a missing feature in several
> discussions on list since the previous proposal was declined. This
> includes:
>
>
> [RFC] [Discussion] Support object type in BCMath [1]
>
> Native decimal scalar support and object types in BcMath [2]
>
> Custom object equality [3]
>
> pipes, scalar objects and on? [4]
>
> [RFC][Discussion] Object can be declared falsifiable [5]
>
>
> The request to support comparison operators (>, >=, ==, !=, <=, <, <=>)
> has come up more frequently, but particularly in discussion around
> linear algebra, arbitrary precision mathematics, and dimensional
> numbers (such as currency or time), the rest of the operators have also
> come up.
>
>
> Typically, these use cases are themselves very niche, but the
> capabilities operator overloads enable would be much more widely used.
> From discussion on list, it seems likely that very few libraries would
> need to implement operator overloads, but the libraries that do would
> be well used and thus MANY devs would be consumers of operator
> overloads.
>
>
> I want to discuss what changes to the previous proposal people would be
> seeking, and why. The most contentious design choice of the previous
> proposal was undoubtedly the
operator keyword and the decision
> to
> make operator overload implementations distinct from normal magic
> methods. For some of the voters who voted yes on the previous RFC, this
> was a "killer feature" of the proposal, while for some of the voters
> who voted no it was the primary reason they were against the feature.
>
>
> There are also several technical and tangentially related items that
> are being worked on that would be necessary for operator overloads (and
> were originally included in my implementation of the previous RFC).
> This includes:
>
>
> 1. Adding a new opcode for LARGER and LARGER_OR_EQUAL so that operand
> position can be preserved during ALL comparisons.
>
> 2. Updating ZEND_UNCOMPARABLE such that it has a value other than -1,
> 0, or 1 which are typically reserved during an ordering comparison.
>
> 3. Allowing values to be equatable without also being orderable (such
> as with matrices, or complex numbers).
>
>
> These changes could and should be provided independent of operator
> overloads. Gina has been working on a separate RFC which would cover
> all three of these issues. You can view the work-in-progress on that
> RFC here:
>
> https://github.com/Girgias/php-rfcs/blob/master/comparison-equality-semantics.md
>
>
> I hope to start off this discussion productively and work towards
> improving the previous proposal into something that voters are willing
> to pass. To do that, I think these are the things that need to be
> discussed in this thread:
I voted in favor of the RFC last time around, and assuming an essentially similar RFC is submitted
again will most likely vote in favor again. I do believe this is a useful "surgical"
feature; not typically used, but when used, very valuable.
> 1. Should the next version of this RFC use the operator
> keyword, or
> should that approach be abandoned for something more familiar? Why do
> you feel that way?
IIRC, the main argument against operator was that it was a new keyword people would
need to learn, and tools would need to adapt to understand. That is... a curious argument, to me,
as it applies to nearly every new language feature that gets added. We just voted in property hooks
and asymmetric visibility. Both introduce new syntax for both users and tooling to adapt to. Both
passed by substantial margins.
For me, the main argument in favor of the operator keyword is that it allows us to
sidestep a particular challenge: Is the method to override the + operator named __plus() or __add()?
Do those words even mean the right thing? Should * be named __times(), __multiply(), or
__dotproduct()? Depending on what type of data you're working on, any of those could be
accurate, or completely wrong and misleading. Instead writing
operator +(...)
means I know precisely which symbol I'm defining. As a side benefit, we also don't have
to think about what visibility means on an operator, which is just kinda weird.
As an extreme example, Python's popular PathLib uses / as a concatenation
operator, because it "looks like" a path. A PHP implementation of the same could be
something like this:
class Path
{
private array $parts;
public function __construct(string $path) {
$this->parts = array_filter(explode('/', $path));
}
public function __toString() {
return implode('/', $this->parts);
}
// And then one of these:
public function __divide(...) { ... }
// or
operator /(...) { ... }
}
One of those is horribly misleading about what's going on. The other is extremely descriptive.
Now, opponents of operator overloading, or just the operator keyword, would argue that
the above is exactly what they want to avoid. For me, that's exactly what I want to enable.
At some level, it's just a basic philosophical difference.
Another argument I recall is that the operator syntax makes it "natural" and
tempting to extend to user-defined operators, so you could define your own operator+-*&() for an
object to do god knows what. I fully agree, there is a risk to doing that. However, that is not
what this RFC was, and I presume will be, suggesting: The available operators are a built-in fixed
list. If we want to add some new operator that only makes sense for objects (such as a bind
operator, something I'd love), that would be its own RFC that we could argue about. And those
who don't want user-definable operators can readily vote against any future proposal to do so,
while still giving us the benefit of the known operators.
I will also note that, in my research into collections in other languages, providing operator
overloads for collections is extremely common, and in practice I find it very ergonomic. We will
probably want collections as built in classes for performance anyway (whether using a custom syntax
or generics), but I note that as another datapoint where operator overloads make a great deal of
sense, but their "standard arithmetic names" would be very misleading. (They often use
the boolean & and | operators, too, which results in some really nice and very readable code.)
> 2. Should the capability to overload comparison operators be provided
> in the same RFC, or would it be better to separate that into its own
> RFC? Why do you feel that way?
My sense, which I've written about before, is that there are different "sets" of
operators that cluster together. IIRC, I said something like:
A. Comparisons. (<=>, ==, etc.)
B. Arithmetic operators (+, -, *, /).
C. Everything else (concat, etc.)
I personally believe that's the order of importance. If we wanted to just dip our toes into
operator overloading, we could do just set A for now. I'm fine with all three groups being
approved, as I have uses for all of them. I could see having A be the main vote, and B and C being
secondary votes on the same RFC.
> 3. Do you feel there were any glaring design weaknesses in the previous
> RFC that should be addressed before it is re-proposed?
Only minor things, I think. "OperandPosition" is a very long name to type all the time,
even if I can't come up with something better. The operand ordering is kinda weird, but given
how much discussion went into it last time I don't have any alternative that isn't either
worse or more weird, or both.
The "multiply by -1 for <=>" bit I don't fully understand the point of. The
RFC tries to explain, but I don't quite grok it.
For reflection, I'd be inclined to make ReflectionOperator extends ReflectionMethod, rather
than an isOperator() method. That is largely stylistic, I suppose, as I am generally a fan of
making the type system do the work for us whenever possible.
> 4. Do you feel that there is ANY design, version, or implementation of
> operator overloads possible that you would support and be in favor of,
> regardless of whether it matches the approach taken previously? If so,
> can you describe any of the core ideas you feel are most important?
I was fairly happy with the previous version, so proposing that as-is would have my vote. I would
probably oppose including arbitrary symbol overloading at this time.
To me, the most important factors are:
1. It's type-safe, and leverages the type system to "make invalid states
unrepresentable" as much as possible. (I'd put the rules around <=> into this
category.)
2. It allows me to opt-in piecemeal to just those operators that make sense.
3. The performance overhead compared to using a method is minimal.
4. It is future-compatible with further language evolution, to the extent possible. (The
operator keyword helps here.)
I'd love to see this brought up again, and hope there is sufficient interest to do so.
--Larry Garfield