On Mon, Aug 16, 2021 at 12:51 AM Nikita Popov <nikita....@gmail.com> wrote:

> We have a better way to specify markers for the engine: Magic methods. I
> think some people have a mistaken notion that engine-integrated interfaces
> are always better than magic methods. This is only the case if such
> interfaces are actually useful from a type system perspective. For example,
> Countable and Traversable are useful magic interfaces, because you can
> sensibly type against them. The recently introduced Stringable interface
> (for the magic method __toString) falls in the same category.
>
> Conversely, Serializable is actively harmful as a magic interface (apart
> from the other issues with it), because whether an class implements
> Serializable does not determine whether it is serializable -- all objects
> are a priori serializable, the Serializable interface just gives it custom
> serialization behavior. You'll note that the new __serialize/__unserialize
> methods are plain magic methods without an interface. With exception of a
> custom serializer implementation, user code should never be checking for
> Serializable.
>
>
What would be your thoughts for the case of ArrayAccess? The engine
integration can't really be used without multiple methods being
implemented, so some form of contractual implementation does make sense.
Additionally, the interface does provide some useful typing features, such
as ArrayAccess|array. However the interface itself doesn't guarantee much
about the actual implementation while being a prime candidate for something
like the never type proposed here. In the case of ArrayAccess, it sort of
exists to provide operator overloading for []. But I don't think it'll be
the last time that we encounter an engine feature that is best provided
through multiple method implementations.


> The operator overloading case is in between: The interface is not actively
> harmful, but they also aren't useful. Given the lack of generics, it's not
> really possible to write code against a "Multiplyable" interface that
> actually provides a useful guarantee. The interface does not distinguish
> whether "T * T" is valid, or only scalar multiplication "T * float" is
> supported. When working with operator overloads in PHP, I expect usage to
> type against a specific class implementing operator overloading, say Money,
> rather than typing against something like Addable&Multiplyable. The latter
> would accept both Money and Matrix, both of which have entirely different
> rules on the kinds of operands they accept, even if they are, in some
> sense, addable and multiplyable.
>
> Regards,
> Nikita
>

To be honest, I don't imagine there are many cases where the __add() method
would use the Addable interface (assuming it existed) as a way to type the
second operand. That would only guarantee that the *other* operand could
handle the adding if it had to, but doesn't tell the called method anything
about how to access the value that the object represents which needs to be
added. I think it is far more likely that it'll be typed against specific
classes, like you're suggesting, in almost all uses of the feature. If for
no other reason than simply the fact that different values mean different
things to different objects.

Perhaps it would lead to a community standard around types of values that
*should* be interoperable, something like a Numberlike interface, but I
have purposely avoided venturing into that so far in my draft of that RFC
because it seemed too opinionated to me, and I wanted to avoid getting into
the domain-specific implementations that some were suggesting. That could
easily spiral into a multitude of very specific implementations ending up
in core, when they more naturally belong in the context aware user space of
the application they are used in.

I am open to suggestions on alternatives. I will also be thinking about a
form this might take that would address this concern while still providing
a cleaner way for engine-like object shapes to be typed. I feel like this
is one of the less disruptive ways that could be done, but less disruptive
obviously means that it will inherit any lingering issues that may exist in
that area.

Jordan

Reply via email to