On Thu, Aug 19, 2021, at 3:38 AM, Jordan LeDoux wrote:
> Hello again internals!
> 
> Thank you all for the feedback that you've provided on my initial inquiries
> about this feature. Several bits of feedback I've received have resulted in
> changes to the proposal as it stands and made this a better proposal.
> First, the RFC has been moved to the wiki:
> 
> https://wiki.php.net/rfc/user_defined_operator_overloads
> 
> The RFC is very long, as it contains a lot of research into the topic,
> which is part of the reason I'm bringing it to discussion so early. Work
> has also begun on an implementation, for which the draft PR can be viewed
> here:
> 
> https://github.com/php/php-src/pull/7388
> 
> As the PR indicates, there is still a ways to go before a finished
> implementation is ready, however the RFC document appears complete enough
> now to be opened for discussion. Again, as I've indicated before, I'm
> taking my time with this, so there won't be a vote prior to a final
> implementation being reviewable.
> 
> It is possible that this RFC will involve some changes to opcache as well
> as new opcodes for > and >= in order to maintain consistency of operand
> precedence in execution, and that's something I want to take my time with
> and listen to a lot of feedback on. Its impact is currently unknown as the
> implementation for it is unfinished.
> 
> Some key points for this proposal as it is currently:
> 
> - TypeErrors from the operator methods are thrown immediately instead of
> being suppressed.
> - The parameter for the other operand must be explicitly typed; an omitted
> type is not considered mixed for these methods. Reasoning explained in the
> RFC.
>     - Type coercion still occurs as normal when strict types is not used.
> - The return type of the == operator function is restricted to be a bool.
> - The return type of the <=> operator function and all implied operators
> (<, <=, >, >=) is restricted to be int and is normalized to -1, 0, 1.
> - User classes are forced to implement overloads to work with the supported
> operators, resulting in an InvalidOperator exception if unimplemented.
> (This is a BC break, but impact is anticipated to be low)
>     - The <=> operator instead falls back to the existing comparison logic
> if the overload is unimplemented.
>     - The special cases of $obj == null and $obj == false return the
> expected bool value even if the overload is unimplemented.
> - Internal classes silently fail-over to existing operator logic. (This
> mainly affects ==).
> - Enums are allowed to utilize operator overloads, but unlike other
> classes, are not forced to and fall back to existing logic for comparisons.
> - The proposal is for dynamic methods instead of static methods, with
> reasoning explained in the RFC.
> - The identity operator === is not overloadable to prevent possibly
> terrible misuse.
> - Interfaces are not suggested or proposed, with reasoning explained in the
> RFC.


This is an amazingly detailed and well-written RFC.  Thank you!

Comments in no particular order:

- In the enum example, you can make it vastly simpler by copying my previous 
bitwise example from the previous thread, using ->value and ->from().  At the 
very least, all those if-else blocks should be match()es.

- As you seem to be a fan of math terminology (hi friend!), I believe the 
formal way to state commuitivity is "multiplication is commutative over natural 
numbers".  Some of the wording around that and associativity (Ibid.) would 
probably be cleaner with that phrasing.

- The "Separate codebases" risk is odd.  As you note, "the same as anything 
else, duh, copy-pasta will bite you."  I'm not sure why that's even listed.

- The larger risk would be competing implementations that use operators 
differently.  Eg, I could see one collection implementation using / to indicate 
"remove these elements" (because that's the complement of modulo, which is "all 
elements except these"), and another to indicate "give me an array of these 
many equally-sized collection objects."  That would be confusing if you bounce 
between two implementations on different projects.  (Eg, Laravel and Symfony.)  
(We could argue that one of those implementations of / is dumb, but that 
doesn't mean they won't exist.)

- I don't really grok how $queue -= 1 would work.  If that expands to $queue = 
$queue - 1, then the = operator would return the value assigned ($queue, after 
the value is removed), and you would get the next item... how?  I don't know 
how you'd do that, making that syntax nominally valid, but useless.

- "In Python, the comparison operators are not directly commutative, but have a 
reflected pair corresponding with the swapped order. However, each object could 
implement entirely different logic, and thus no commutativity is enforced. "

I... have no idea what that sentence means.  Please clarify.

- It doesn't seem to be explicitly stated, but I presume developers can type 
their operator methods as `mixed` if they are so inclined?  (I don't see why 
they would, but they could.)

- You refer to InvalidOperator as an exception, although it extends Error.  
That's technically incorrect.  It is a new *throwable*, which extends error.  
Please use the correct terms.  (The design is fine, IMO, but it should have the 
correct terminology.)

- I think sticking to compareTo() is probably wise.  I can see all sorts of 
abuses of the other comparison operators if they were allowed... and I'd 
probably do some of that abuse myself, frankly. :-)

- Please make sure via tests that __compareTo() is triggered by sort() and 
friends, as that's the main use case for it: Making sorting objects just like 
sorting anything else, without having to provide a separate, independent 
comparison algorithm.  (Basically, replacing usort() with __compareTo().)

> Open questions:
> 
> - Should this RFC include the bitwise operators or remain limited to the
> minimal set of math operators?

This should IMO be decided based on how easy they are to implement.  If it's 
trivial to include, and uncontroversial, sure.  If it's contentious or hard, 
punt to a later RFC.

> - Should the == operator fail-over to existing behavior for all classes if
> unimplemented, not just internal ones? (This would reduce BC break, but
> make catching implementation errors more difficult for userland code.)

It would help if you documented the current behavior in the RFC, because I had 
to go test it to see what it was. :-)

I'd say yes, keep the existing behavior.  I actually have a few tests right now 
in a serialization library that check if $foo == unserialize(serialize($foo));  
(Not those functions, but in concept.)  

> - Should implicit interfaces be provided for the overloads? (Actual
> interfaces cannot be provided except for if my previous Never For Parameter
> Types RFC were adopted.)

I think the RFC makes a good case that, unlike Stringable, they're not really 
useful.  The reason to have them would be to ask "does this object implement 
this magic method", but as you note that doesn't really tell you much.  
Stringable only has one meaningful operation, so it's meaningful to type 
against.  Addable doesn't give you sufficient information.  (Insert the usual 
comments about Generics here.)

Thanks!

--Larry Garfield

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php

Reply via email to