On Sat, Aug 7, 2021 at 3:29 PM Larry Garfield <la...@garfieldtech.com>
wrote:

>
> Side note: Please remember to bottom-post.
> (trimmed message)
>

> --Larry Garfield
>
> --
> PHP Internals - PHP Runtime Development Mailing List
> To unsubscribe, visit: https://www.php.net/unsub.php
>
>
Sorry about top-posting. :)

Your arguments for why grouping is undesirable are very persuasive. While I
don't think that having exceptions thrown from stubs is necessarily a bad
thing, I definitely understand how this suggests that the grouping is
unsuccessful at "hinting" the developer on implementation, and also how the
grouping may be too restrictive as a language-level feature. ArrayAccess
really is an interesting comparison.

I did very briefly consider the idea of implementing a *single* magic
method: __doOperation(string $operator, mixed $lhs, $rhs = null)

After thinking on it briefly, I came to many of the same conclusions: this
would result almost invariably in further fracturing between a "Laravel"
way of doing things and a "Symfony" way of doing things which would be
wholly incompatible and inconsistent. It would also be... complex for IDE's
to support in any meaningful way, especially for custom operators that are
not part of the interpreter. (It also would be far more complex for me to
implement as it would probably require some careful changes to the
compiler.)

So I'll simply disqualify the idea of custom operators right now. I do
understand, like you, why some people might want them and use them, but I
feel like it's too complex and risky of a change, and at its core, not a
change that I want to make, so someone else would need to actually do the
RFC if that was the case.

> The 4 arithmetic operators, concat, and compare.

I would argue that both modulo and pow (% and **) are arithmetic operators
that should come with the basic four as well. Meanwhile, I think the concat
operator is both more prone to error *and* less useful. Currently, if you
try to concat an object, it will be cast to a string, and since
__toString() already exists, this behavior can effectively be controlled
already. The main reason that other operators couldn't easily be solved
with supplementary __to*() methods is that for *many* usecases where you'd
benefit from overloading operators, the scalar types don't adequately
capture the state (which is presumably the reason that the program is
representing them as objects).

Further, as detailed in my earlier reply, I don't think we can get away
with only allowing compare. Complex numbers are a trivial example of why,
but there are many others where only some of the comparison operators make
sense. We could, however, allow compare as a minimal implementation to
support the others. If it is implemented, then the others can be derived,
but if it is not then the other comparison operators should be able to be
implemented individually.

> None of these approaches resolves the commutability problem. There is no
guarantee that $a + $b === $b + $a, if $a or $b are objects that implement
Addable.

I don't think this is a problem which can be resolved without some
extremely strict limitations on the operator overloading. (Something like,
objects can only be on both sides if they are the *same* class.) Even then,
there is little we could do to prevent the user code from doing something
like:

function __add($rhs) {
    return (random_int(0, 1) == 1 ? true : 5);
}

As such, I think the idea of operator overloading is fundamentally
incompatible with the concern of ensuring commutability. In fact, *many* of
the usecases for this feature would *depend* on them not being commutable.
For matrices, A * B != B * A except under specific values. This is in fact
part of the definition of multiplication under matrices, so I feel that
wanting to ensure commutative behavior actually represents misunderstanding
the feature itself. Extending the native operators to work with matrices
using an internal implementation would *also* result in non-commutative
behavior, because that's the *correct* behavior for matrices.

I do not see this as inherently a problem to be solved, but it is a thing
to keep in mind when considering the implementation.

> Should the methods in question be dynamic or static? In my mind, the only
argument for static is that it makes it more likely that they'll be
implemented in an immutable way.

I also think that dynamic methods are entirely possible here. While
immutability is an inherent property of using operators in nearly any
context, it's not something that we can guarantee when we leave the
implementation of operator handling up to userspace, and requiring statics
won't do that either. Laravel as an example uses static properties and the
__callStatic() method to provide mutable static calls everywhere in the
entire framework with Facades (something there are very strong opinions
about in userland).

As such I think that requiring static methods is a way to make internals
"feel better" about it without actually providing any real guarantees of
consistency or immutability.

> What if any type enforcement should the language force?

Personally I do not think that any type enforcement should be required. I
think it should be *possible* if the application wants it, but I don't
think it should be required.

Jordan

Reply via email to