Hi Sara,
Sara Golemon wrote:
Patricio Tarantino has asked me to help him propose Operator
Overloading in PHP 7.1 (based in part on my operator extension in
PECL). I think we can expose this to usespace as magic methods with
very little overhead (the runtime check and dispatch is already there,
after all).
I do think that the "Future Expansion" section bears following through
with as well, but the basic set of methods already hooked for GMP
would be a nice start.
https://wiki.php.net/rfc/operator-overloading
While I would like to see operator overloading in PHP, I don't
particularly like the approach this RFC takes. It seems to take the
approach C++, Python and so on use where you can simply overload any
operator as you see fit. While that is arguably useful, it is open to
abuse. In C++, for example, you can join two file paths by dividing
them, or write to a stream by bitwise shifting it left by another
stream. In Python, you repeat a string by finding the product of it and
an integer. Abusing operator overloading like this harms readability, is
not necessarily intuitive and is anyway unnecessary: a function or
method would work just as well in these situations. Also, I'm not sure
it's a good fit for a dynamic language to make operators do different
things depending on their operand types. That just creates the
possibility of unpleasant surprises at runtime, and PHP has enough of
these already without the possibility of users creating more.
Luckily, C++ and Python's approach is not our only option. I am quite a
fan of Haskell's approach to operator overloading, which is less prone
to abuse. In Haskell, a purely-functional programming language, certain
operators (and also certain math functions like abs()) are defined as
part of "typeclasses", somewhat akin to interfaces in classical
object-oriented languages like Java or PHP. These typeclasses group
related operations together, and a conforming implementation of that
typeclass must implement all the operations. For example, there is a
`Num` typeclass[0] for numbers, which defines the addition, subtraction,
multiplication, negation, absolute value, sign and
conversion-from-integer operations. Extending `Num`, there is a
`Fractional` typeclass[1] for fractional number types, which adds
fractional division, reciprocal and conversion-from-rational operations.
There are likewise similar typeclasses for non-numbers, and other
descendents of `Num` for other kinds of numbers. The approach Haskell
takes here is not as prone to abuse because you cannot simply implement
operations as you please: you must implement them as a set. (Though,
granted, Haskell also discourages overloading abuse by actually letting
you define custom operators.) With this approach Haskell also achieves
something else useful, in that you can use typeclasses as a type
constraint on function parameters and return types. I think it would be
more worth pursuing a Haskell-style approach in PHP, most likely with a
hierarchy of magic interfaces.
All that aside, I have some other issues with the RFC. The set of
overloadable operators proposed is very small, and I'm surprised it
doesn't even include the full set of number operations. But
additionally, if you were to fill in that set, I wonder if it might be
worthwhile making some of the math functions overloadable as well, since
otherwise you have an incomplete set of operators: %'s complement is
intdiv(), not /, and the closest thing we have to a complement of / is
fmod(). Likewise, since abs() works on integers and floats, perhaps it
should work on all number types. I don't think GMP currently overloads
any of these, though, and there is the possibility we might create
confusion if overloading extended to math functions.
Another concern I have is the possibility of having __assign_*
overloads. These would useful for avoiding having to instantiate a new
object, but there's potential for them to be implemented wrongly, or be
implemented without their corresponding normal operations. Since we
don't have any way to make PHP 4-style copy-on-write value classes at
the moment, I think we should just do the simpler thing and assume we're
dealing with completely immutable objects, and therefore not have
__assign_* methods. Similarly, I don't think should have overloads for
++ and -- (the proposed /__(post|pre)_(inc|dec)/ methods). Heck, they're
probably not even that useful, because you could simply check in __add
or __sub if the operand's absolute value is 1.
Regarding the possibility of comparison operators, again I think we
should go the simpler route. For <, <=, >, and >=, we only need one
overload method, perhaps __cmp, which returns the usual negative, zero
or positive value to indicate ordering. I can't see a benefit of having
a seperate methods for each of these, and it would introduce the risk of
them being implemented inconsistently, creating chaotic sorts. Similarly
for == and !=, we only need a single method, perhaps __is_equal, or
maybe we should not give them a separate method and group them with <,
<=, > and >= into __cmp, which would again have the benefit of
preventing inconsistent implementation. For === and !==, I would be wary
of letting them be overloaded, given they are currently "strict", and it
is useful to be able to check if two objects are the same instance,
something objects shouldn't need control over. == and !=, on the other
hand, compare content, which the objects themselves can probably decide
better than PHP's default behaviour.
Anyway, I appreciate you making this RFC, if only to prompt discussion.
Thanks.
[0] http://hackage.haskell.org/package/base-4.8.1.0/docs/Prelude.html#t:Num
[1]
http://hackage.haskell.org/package/base-4.8.1.0/docs/Prelude.html#t:Fractional
--
Andrea Faulds
http://ajf.me/
--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php