RFC Operator Overloading in Userspace

php.internals

Sara Golemon

10 years ago
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 -Sara

Davey

10 years ago
I would love to see all of the is_* methods implemented in the initial implementation, these are far more useful to me than the basic math operators. As for interfaces, I'm generally pro-interfaces as programmatic documentation if nothing else — however, none of the magic methods have implemented this except for the JsonSerializable stuff. But, I'm all for moving the language forward on new features only and not getting caught in the "legacy consistency" trap again. We can maybe add optional interfaces for other magic methods, and then make them required in the next major. I've been hoping for operator overloading in userland since the GMP changes and welcome this proposal in general. Thanks Patricio and Sara both! :) On Sun, Jan 3, 2016 at 2:14 AM, Sara Golemon <pollita@php.net> wrote:

Bishop Bettini

10 years ago
Hi Sara and Patricio, On Sat, Jan 2, 2016 at 9:14 PM, Sara Golemon <pollita@php.net> 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).
+1 to moving this out of PECL and into the engine.
> 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. >
Whoa, that's a big list of magic methods. I wonder if a generic dispatch magic method would be more developer friendly. Something like: public function __overload(string $operator, object $rhs = null); // $rhs === null iff op is unary Thanks! bishop

Stas Malyshev

10 years ago
Hi!
> 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).
We could, but I'm not sure whether we should. One of the reasons why userspace operator overloading was kept out was that a lot of its uses make code worse, not better. When you see expression that looks like addition but you know it can do literally anything, that makes reading the code so much more frustrating. I mean, you can *hope* * still means "multiply", but you can't know it anymore. And there are not that many types for which * has natural meaning. Additionally, introducing about 9000^W^W a lot of new magics drastically raises both cognitive complexity of the language (now we have 14 magics, adding operators (and RFC lists only arithmetic ones, though one may say why stop there - C++ certainly didn't) more than triples that count. Also, implementing them in consistent fashion would be non-trivial, and imagine what fun would be had when +, += and ++ work differently (btw, why have different implementations for + and += at all?). And then we get questions of commutativity and associativity... What I'm trying to say, doing proper operators set is not that trivial, and besides a very small set of use cases, I'm concerned it would be used to write very clever write-once read-never code instead of good code.
-- Stas Malyshev smalyshev@gmail.com

Sara Golemon

10 years ago
On Sat, Jan 2, 2016 at 6:59 PM, Stanislav Malyshev <smalyshev@gmail.com> 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). > > We could, but I'm not sure whether we should. One of the reasons why > userspace operator overloading was kept out was that a lot of its uses > make code worse, not better. When you see expression that looks like > addition but you know it can do literally anything, that makes reading > the code so much more frustrating. I mean, you can *hope* * still means > "multiply", but you can't know it anymore. And there are not that many > types for which * has natural meaning. >
I can't argue with you there. Magic can be exceedingly dangerous in the wrong hands. I think the core question of this RFC is: Is the responsibility to use magic safely something to grant PHP developers. I'm personally not decided on it, which is why operator stayed a PECL extension and it's taken someone else to put the RFC forward (even if I am sponsoring him to get the discussion rolling).
> Additionally, introducing about 9000^W^W a lot of new magics drastically > raises both cognitive complexity of the language (now we have 14 magics, > adding operators (and RFC lists only arithmetic ones, though one may say > why stop there - C++ certainly didn't) more than triples that count. >
I think this can be handled by taking Bishop's suggestion. After all, it's how it's actually implemented internally via do_operation. We could apply the same to the userspace callback and simplify the implementation details, even if it add slight complexity to script implementations.
> Also, implementing them in consistent fashion would be non-trivial, and > imagine what fun would be had when +, += and ++ work differently >
Hilarious to hear you cite that as the already existing inconsistency between += and ++ with non-numeric strings came up only last week. $x = 'foo'; $x++; // $x === 'fop' $x += 1; // $x === 1 But I digress, your point is taken, and it comes back to the same thing in my first paragraph response. I think we can all agree that the potential for evil is great, the question is whether or not we trust developers with great power.
> (btw, why have different implementations for + and += at all?). >
Because the former would typically result in a new object, the latter wouldn't. In the case of GMP it's the difference between: $x = new GMP(1); $x = $x + 1; // Makes a new GMP instance and replaces $x $x = new GMP(1); $x += 1; // Updates the internal value of the GMP instance That's a significant difference in terms of semantics if there are other references to $x lying around. Nevermind the cost of newing/cloning/destroying objects when all you really wanna do is mutate an existing one a bit.
> And then we get questions of commutativity and associativity... >
Yeah, this was always one of my annoyances of pecl/operator. Honestly, I could see making them always-left-associative to avoid some unforseen wtf. But (if combined with the additional operations) would require resolving the commutativity that happens in ZEND_AST_GREATER* (not terribly complex to handle, but we'd have to decide to do it).
> What I'm trying to say, doing proper operators set is not that trivial, > and besides a very small set of use cases, I'm concerned it would be > used to write very clever write-once read-never code instead of good code. >
Not arguing with that at all. :) -Sara

Stas Malyshev

10 years ago
Hi!
> Hilarious to hear you cite that as the already existing inconsistency > between += and ++ with non-numeric strings came up only last week. > > $x = 'foo'; > $x++; // $x === 'fop' > $x += 1; // $x === 1
Exactly, because on strings ++ and += 1 are not the same operator. And, as you noted, it requires a pause to figure it out. But now imagine somebody overrides + and ++ to work on files, doing whatever sounded clever at the moment - how easy would it be to read that code? To maintain it? That's my concern. I do agree that is a question of judgement if we want this kind of magic. Right now I am leaning to the "no" side, especially given the RFC lacks the description of the actual use cases. I mean, the coolness factor is there, all the cool boys - C++, Python, Swift, Scala - do that, but I'd like to see what are the use cases specifically for PHP.
> That's a significant difference in terms of semantics if there are > other references to $x lying around. Nevermind the cost of > newing/cloning/destroying objects when all you really wanna do is > mutate an existing one a bit.
Right. But properly covering all this would require either a lot of boilerplate code or some very tricky engine coding. BTW, I don't think for GMP right now += modifies existing object. I guess it may be possible to implement it the other way (not sure).
-- Stas Malyshev smalyshev@gmail.com

Sara Golemon

10 years ago
> BTW, I don't think for GMP right now += modifies existing object. > I guess it may be possible to implement it the other way (not sure). >
It doesn't. As seen below, we get a new, second object on the assign_add. Wrong behavior IMO, but something to fix separately from this. $g = gmp_init(123); var_dump($g); // object(GMP)#1 { ["num"]=> 123 } $g += 1; var_dump($g); // object(GMP)#2 { ["num"]=> 124 } -Sara

John Bafford

10 years ago
Hi Sara, On Jan 2, 2016, at 21:14, Sara Golemon <pollita@php.net> 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 > > -Sara
While I don’t necessarily think adding operator overloading is a great idea, I think if it’s to be done, we could do better. I’d offer two suggestions: 1) rather than the list of magic function names, use the actual operators themselves
> function ^($value)
or
> function operator <<($value)
This way, new magic methods don’t have to be created in the event we created new operators later. (Unlikely, I know.) To handle ambiguity between prefix/postfix/infix operators, you could use something like:
> function prefix -($value)
> function infix -($left, $right) > function postfix ++($value)
This would also address an issue in the proposal as written: __add and __sub don’t handle the ambiguity of prefix + and - operators. Bishop Bettini suggests:
> public function __overload(string $operator, object $rhs = null); // $rhs === null iff op is unary
but you really need separate functions (or an additional parameter) to handle that case. You can’t only pass in null because then you can’t disambiguate between $foo - null and -$foo. 2) I think we should take a look at Swift’s operator definition mechanism. Besides just allowing for operator overloading, Swift also allows you to specify entirely custom operators. For example (from the Swift Programming Language book, Language Guide, Advanced Operators chapter):
> “New operators are declared at a global level using the operator keyword, and are marked with the prefix, infix or postfix modifiers: > >> prefix operator +++ {} > > The example above defines a new prefix operator called +++. This operator does not have an existing meaning in Swift, and so it is given its own custom meaning below in the specific context of working with Vector2D instances. For the purposes of this example, +++ is treated as a new “prefix doubling incrementer” operator. It doubles the x and y values of a Vector2D instance, by adding the vector to itself with the addition assignment operator defined earlier: > >> prefix func +++ (inout vector: Vector2D) -> Vector2D { >> vector += vector >> return vector >> }”
> “The following example defines a new custom infix operator called +-, with left associativity and a precedence of 140:” > >> “infix operator +- { associativity left precedence 140 } >> func +- (left: Vector2D, right: Vector2D) -> Vector2D { >> return Vector2D(x: left.x + right.x, y: left.y - right.y) >> }”
Operators can be defined as prefix, infix, or postfix. Infix operators can specify associativity (left, right, or none (default)) and precedence. Swift limits the first character of an operator is limited to one from a set of typical operator symbols and a set of unicode characters, so you can’t create or overload operators using arbitrary ascii characters. In PHP, we might imagine this example to look like:
> //in global scope > operator infix +- associativity(left) precedence(140); > function +-($left, $right) { … }
In Swift, operator definitions and overloads are done in on a global basis (and multiple definitions are resolved appropriately with Swift’s type system). This makes sense for Swift, but it would be more difficult for PHP, given PHP’s more flexible type system. I’m unclear what the best approach is to handle typehints, which in this case are more part of the operator functions logic name than just run-time limitations on parameters. The major problem I see with this, besides the parser complexity it would entail, is that it would make static analysis that much harder, and you wind up with PHP code that can’t actually compile unless the operator definitions are loaded. -John

Levi Morrison

10 years ago
On Sat, Jan 2, 2016 at 7:14 PM, Sara Golemon <pollita@php.net> 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
My ISP is having issues with no resolution in sight and I can't view the RFC right now. So sorry for asking a question that is probably answered in the RFC: this proposes using magic methods, not interfaces, correct? If so, what is the justification?

Nikita Popov

10 years ago
On Sun, Jan 3, 2016 at 3:14 AM, Sara Golemon <pollita@php.net> 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 > > -Sara >
Thanks for the proposal, Sara! A few questions to clarify: 1. If an object implements __add__, will $a += $b be equivalent to $a = $a->__add($b) and ++$a be equivalent to $a = $a->__add(1)? This is how internal operator overloading currently works (we have no separate overloads for assign ops). Will this RFC also work this way? 2. Regarding associativity, the RFC states "When both operands to a binary expression implement operator overloading, the left-hand operand's handler will be invoked. If only one of the two implement overloading, then that side is invoked with the relative operation inverted as necessary." This sounds rather fishy to me, as not all operations are abelian. In particular __sub, __div, __pow and __concat are *usually* non-commutative. As such, the current RFC does not appear to leave room for supporting things like 1/$complex or 1-$complex or e**$complex. As such I wonder if these magic methods oughtn't be static methods with two operands. 3. Relatedly, consider the situation where $obj1 op $obj2, where $obj1 and $obj2 are instances of different classes, both of which have distinct overloads of the op operator. It so happens that the op-implementation of $obj1 does not support operations against $obj2, while the op-implementation on $obj2 does support operations against $obj1. The op-implementation of $obj1 will be invoked first -- will there be some way for it to signal "I don't support this operation", thus allowing the op-implementation of $obj2 to be used? Thanks, Nikita

Sara Golemon

10 years ago
> On Jan 3, 2016, at 02:11, Nikita Popov <nikita.ppv@gmail.com> wrote: > Thanks for the proposal, Sara! A few questions to clarify: >
One point of order. It's Patricio's peoposal, I'm just helping him move it along.
> 1. If an object implements __add__, will $a += $b be equivalent to $a = $a->__add($b) and ++$a be equivalent to $a = $a->__add(1)? This is how internal operator overloading currently works (we have no separate overloads for assign ops). Will this RFC also work this way?
To be quite honest, I'd rather it DIDN'T work that way. As I said to Stas, I regard our current crossloading as broken since it forces new object creation when the statement visually calls for mutation. Semantically, what we do for GMP right now isn't sound, even if the distinction doesn't seem to be breaking anyone.
> > 2. Regarding associativity, the RFC states "When both operands to a binary expression implement operator overloading, the left-hand operand's handler will be invoked. If only one of the two implement overloading, then that side is invoked with the relative operation inverted as necessary." This sounds rather fishy to me, as not all operations are abelian. In particular __sub, __div, __pow and __concat are *usually* non-commutative. As such, the current RFC does not appear to leave room for supporting things like 1/$complex or 1-$complex or e**$complex. As such I wonder if these magic methods oughtn't be static methods with two operands. >
I did consider that as an alternate option. function overload_register($op, $lhtype, $rhtype, $callback); overload_type(ZEND_ADD, 'Complex', 'any', 'Complex::add'); This would get pretty expensive in terms of large lookup tables though, so I'm hesitant there.
> 3. Relatedly, consider the situation where $obj1 op $obj2, where $obj1 and $obj2 are instances of different classes, both of which have distinct overloads of the op operator. It so happens that the op-implementation of $obj1 does not support operations against $obj2, while the op-implementation on $obj2 does support operations against $obj1. The op-implementation of $obj1 will be invoked first -- will there be some way for it to signal "I don't support this operation", thus allowing the op-implementation of $obj2 to be used? >
I do like the idea of being able to signal type failures. Would a specific exception be too heavyweight? Or just return "ConversionFailure" object instance or something... -Sara

François Laupretre

10 years ago
Hi,
> I did consider that as an alternate option. > > function overload_register($op, $lhtype, $rhtype, $callback); > > overload_type(ZEND_ADD, 'Complex', 'any', 'Complex::add'); > > This would get pretty expensive in terms of large lookup tables though, so I'm hesitant there.
This is much better, IMO, as the syntax can be extended to every type hints (including union types in the future). It also allows to overload operators on scalar types (operation on arrays for instance). Magic methods just don't fit there. But I'm not sure we should overload at the opcode level, as the userspace method should receive different codes for '+=' and '+'. Anyway, supporting scalar types, union types, and 'any' without loosing too much performance is far from obvious. Regards François

Mark Baker

10 years ago
On 03/01/2016 02:14, 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). > > https://wiki.php.net/rfc/operator-overloading
It's something I'd love to see, and would be particularly useful for my Complex Number and Matrix libraries, but (as Bishop says) the large list of magic methods does feel excessive, and a single dispatcher method feels simpler. But how then would it be possible to overload several operators? If the __overload() dispatcher method has to implement a switch on the value of the operator argument, and implement error handling for invalid or non-implemented operators, it's pushing a lot of complexity into that magic method
-- Mark Baker _________ |. \ \-3 |_J_/ PHP | || | __ | || |m| |m| I LOVE PHP

Larry Garfield

10 years ago
On 01/02/2016 08:14 PM, 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 > > -Sara
I'm really torn here. First off, IF PHP were to adopt such overloading, I would much prefer special interfaces over magic methods. It's simply far more self-documenting and reduces the difference between user-space and PHP-space features. (And I'd be all for migrating the current magic methods to that, too, if we wanted consistency.) That said... as Stas notes the can of worms this entails is quite large. I've started thinking more in terms of pure value objects in the past year (PSR-7-inspired), and for those, having richer operations can be quite useful. DateTime, for instance, supports > and <, which would be similarly helpful on other value objects. Think money for a good example, or object-based collections. It also serves to improve the type system, as a Currency object could allow itself to be added iff the other Currency object was in the same denomination. That said, the potential for magic "WTF were you thinking?" there is also scarily large. Stas hinted at an example, as does the RFC itself. From the first example: public function __add($rhs) { $ret = clone $this; if ($rhs instanceof Complex) { $ret->real += $rhs->real; $ret->imaginary += $rhs->imaginary; } else { $ret->real += (int)$rhs; } return $ret; } That all seems reasonable. Return a new object that is the result of adding the real and imaginary components together. Cool. I give it about 8 minutes before someone forgets to clone $this first and just starts returning $this, in which case you now have an addition operator that both modifies a value in place *and* returns itself. I then give it about 4 minutes after that before someone tries to defend that as a good practice, and now we have a large cadre of people who will insist that $c = $a + $b; modifying $a along the way is "totally a legit thing, it's a performance optimization, stop being so pedantic telling me I shouldn't do that!" (Come on, you know that's going to happen.) The worst-case scenario there is not guaranteed, but it still scares the bejeebus out of me. So for the moment, I'm tentatively -1. Perhaps limiting it for now to the obviously immutable operators, ie the comparison operators only, would be a safe babystep? --Larry Garfield

Derick Rethans

10 years ago
On Sat, 2 Jan 2016, 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
"Limiting Issue: Greater/Lesser are not distinct" That should be addressed in the engine first too (or with) this change. However, operator overloading... cheers, Derick

Andrew Faulds

10 years ago
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/

Rowan Collins

10 years ago
On 04/01/2016 16:34, Andrea Faulds wrote:
> 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. [...] I think it would be > more worth pursuing a Haskell-style approach in PHP, most likely with > a hierarchy of magic interfaces.
For those not familiar with type classes, it's worth mentioning something you clarified to me over Twitter some time ago, about how they differ from interfaces (correct me if I go wrong). With an interface, the definition of GMP could include "implements NumLike", meaning that any instance of GMP "is a NumLike object"; there are only two entities to define: the interface, and the class which implements it. With a type class, GMP itself wouldn't implement NumLike, you would have a third entity, which was "an instance of NumLike for GMP objects"; GMP would "have a NumLike type class", defined externally to it. This goes some way to addressing the question of which operand receives the message that an overloaded operator has been invoked - the implementation would not have a "$this". As I understand it, multi-parameter type classes even allow you to bridge between types that aren't aware of each other - e.g. define the result of gmp_init(42) + \Litipk\BigNumbers\Decimal::create(42) Incidentally, my main exposure to operator overloading and custom operators is in PostgreSQL, which implements them as a special case of overloaded function despatch: you can have two functions "add(int, int)" and "add(float, float)", which will be selected between when a statement is compiled; you can then define the binary operator "+" for types "int, int" maps to function "add(int, int)", and it's dispatched the same way. Again, this removes the question of which operand should receive the message, because the operator (and its associated function) are not part of the type - if you want to add an int to a float, you can / must define both "operator + (int, float)" and "operator + (float, int)". Like in C++, though, Postgres's operators aren't intrinsically grouped, and it's up to you to use them in idiomatic ways, such as "||" for various types of concatenation (because SQL) and "@>" for "contains". The major disadvantage to separating the implementation of the operator / type class from the class definition is that I can't see how it would work with autoloading. Regards,
-- Rowan Collins [IMSoP]

Andrew Faulds

10 years ago
Hi Rowan, Rowan Collins wrote:
> On 04/01/2016 16:34, Andrea Faulds wrote: >> 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. [...] I think it would be >> more worth pursuing a Haskell-style approach in PHP, most likely with >> a hierarchy of magic interfaces. > > For those not familiar with type classes, it's worth mentioning > something you clarified to me over Twitter some time ago, about how they > differ from interfaces (correct me if I go wrong). With an interface, > the definition of GMP could include "implements NumLike", meaning that > any instance of GMP "is a NumLike object"; there are only two entities > to define: the interface, and the class which implements it. With a type > class, GMP itself wouldn't implement NumLike, you would have a third > entity, which was "an instance of NumLike for GMP objects"; GMP would > "have a NumLike type class", defined externally to it.
That's correct, yes. In Haskell, the data type definition, the type class definition, and the the type class implementation are all separate, which is different from Java-style interfaces (which PHP has) where you combine interface implementation with data type definition.
> This goes some way to addressing the question of which operand receives > the message that an overloaded operator has been invoked - the > implementation would not have a "$this". As I understand it, > multi-parameter type classes even allow you to bridge between types that > aren't aware of each other - e.g. define the result of gmp_init(42) + > \Litipk\BigNumbers\Decimal::create(42)
Indeed, since type class implementations are separate from the data types they apply to and the definition of the typeclass. Data types in Haskell themselves don't really have awareness of other things anyway, Haskell doesn't have objects. (A nitpick: at least in the case of `Num` type class, where + is defined, Haskell doesn't actually let you define how to add one type to another, at least not directly, since it has only one type parameter. Instead, you have to do an explicit conversion if you want to do such an addition. And anyway, it's true that *for multi-parameter type classes*, what you describe would be possible.) And yes, you can make an implementation of operators yourself for types which don't already have one.
> The major disadvantage to separating the implementation of the operator > / type class from the class definition is that I can't see how it would > work with autoloading.
Yeah, I don't know how well type classes for operator overloading would work in PHP. I was thinking of using interfaces instead for PHP operator overloading, since they're what we already have and are familiar, even if they lack some of type classes' benefits. The problem with interfaces, though, is the $this one, as you mentioned. That's a bit of a headache. Thanks for your input!
-- Andrea Faulds https://ajf.me/

Stas Malyshev

10 years ago
Hi!
> 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.
I agree that interface approach looks better than Python/C++ approach for PHP. One thing it also allows is introducing things in a gradual and conceptually sound manner - i.e. Comparable, Numeric, etc. which would implement specific operator set instead of just letting people implementing random bag of operators. I'm not sure how to make it technically but it looks better to me conceptually. We don't have as powerful type system as Haskell but I think we still can do a lot with interfaces.
> 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.
Which reinforces the point above that maybe before the are tackling the operators in userspace we need to identify use cases and create structure for them, and then it might be easier for us to avoid inconsistencies. We're kind of moved forward with GMP without doing that, but fortunately it's easier to fix such things while it's confined to the engine/extensions. Once it's in userspace, changing it would be way harder.
-- Stas Malyshev smalyshev@gmail.com

Andrew Faulds

10 years ago
Hi Stas, Stanislav Malyshev wrote:
> Hi! > >> 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. > > I agree that interface approach looks better than Python/C++ approach > for PHP. One thing it also allows is introducing things in a gradual and > conceptually sound manner - i.e. Comparable, Numeric, etc. which would > implement specific operator set instead of just letting people > implementing random bag of operators. I'm not sure how to make it > technically but it looks better to me conceptually. We don't have as > powerful type system as Haskell but I think we still can do a lot with > interfaces.
I agree that we could do something with interfaces. I would like to point out that we actually already have an example of this, in the form of the \ArrayAccess interface, which requires you to implement all the different indexing operations at once. Unfortunately, though, \ArrayAccess doesn't give us a precedent for dealing with the $this issue (in `$a + $b`, who gets called, how do we handle differing types, etc?), but it's a start.
>> 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. > > Which reinforces the point above that maybe before the are tackling the > operators in userspace we need to identify use cases and create > structure for them, and then it might be easier for us to avoid > inconsistencies. We're kind of moved forward with GMP without doing > that, but fortunately it's easier to fix such things while it's confined > to the engine/extensions. Once it's in userspace, changing it would be > way harder.
In full agreement there. On that note, it's of course possible to support overloading certain things, but only internally. It might be reasonable to expose __assign_add (well, its C equivalent) to GMP but not to userland, for instance. Regarding use cases, there are a few that come to mind. One of these is userland number types. We don't, to the best of my knowledge, currently have a decimal type as a PHP extension, so applications which need it must look to userland implementations (often backed by bcmath underneath). These would be more pleasant and intuitive to use if they could use our normal arithmetic operators. For such userland types, I imagine an interface with basic arithmetic operations would work. Perhaps we might call it \Number. A question might be whether to split apart integer and float operations like Haskell does. I'm not sure what fits this use-case best, though it's probably better not to require classes to implement what they don't need, and just let people get an error if they do, say, ($decimal % 5). Another use case is userland string types. There's several projects out there which wrap PHP strings in an object to provide an object-oriented interface which is more pleasant to use than PHP's string API. There is also the use-case of Unicode string wrappers, since PHP still doesn't have a Unicode string type. They would benefit from being able to overload the concatenation operator, and possibly also the binary operations (^, & and |) which work on strings. Everything else these types need is already overloadable with the existing \ArrayAccess and \Iterator(Aggregate). There is a problem here, through, in what we should require such classes to implement beyond concatenation. Requiring nothing else leaves it open to abuse (I can already imagine `.` being used for the dot product), but requiring, say, binary operations would be unfair (they're nonsense in the context of Unicode), as would \ArrayAccess (Unicode strings may have non-constant time indexing) or \Traversable (a Unicode string class might choose to lack a default iterator to force the user to consider the different ways a Unicode string can be used). Maybe we could require \Countable, but even that has problems regarding Unicode, non-constant time complexity especially. So, I'm not quite sure what to do there. Thanks.
-- Andrea Faulds https://ajf.me/

Larry Garfield

10 years ago
On 01/04/2016 05:04 PM, Andrea Faulds wrote:
> I agree that we could do something with interfaces. I would like to > point out that we actually already have an example of this, in the > form of the \ArrayAccess interface, which requires you to implement > all the different indexing operations at once. Unfortunately, though, > \ArrayAccess doesn't give us a precedent for dealing with the $this > issue (in `$a + $b`, who gets called, how do we handle differing > types, etc?), but it's a start.
As a non-Haskell-knowing dev I only followed a portion of the conversation leading up to this, but I think I'm tentatively liking the direction this is moving. If I understand correctly, the suggestion is to add, eventually, e.g.: interface Comparable { public function compare($other) : int; (returns -1, 0, 1) } interface ArithmeticStuff { public function add($other) : ArithmeticStuff; public function subtract($other) : ArithmeticStuff; // ... } Am I following? If so, I see the following (possibly surmountable) concerns: 1) The ArithmeticStuff interface (obviously better named) still has no way to prevent mutation of $this or $other. I still see that as user-space trainwreck waiting to happen unless there's some way to force those methods to NOT modify $this in any way. 2) Besides the issue with different classes that both implement the same operator interface, as Andrea notes, it also would cause issues with subclasses. To wit: class Money implements Numberish { protected $valueWithoutDecimals; public function add($other) { $ret = new Money($this->valueWithoutDecimals); $ret->valueWIthoutDecimals += $other->valueWithoutDecmials; return $ret; } } class Dollars extends Money {} $d1 = new Dollars(4); $d2 = new Dollars(5); $d3 = $d1 + $d2; $d3 would now be an instance of Money, NOT Dollars. Sure, there are better ways of writing that implementation that would avoid that problem (cloning $this, using get_called_class(), etc.) but that the potential is there is worrisome. Possible solution to 1 and 2: Instead of just passing $other, pass $clone and $other, and block $this. To wit: class Money implements Numberish { protected $valueWithoutDecimals; // Being static, $this doesn't exist although it's what $clone was cloned from. public static function add($clone, $other) { $clone->valueWIthoutDecimals += $other->valueWithoutDecmials; return $clone; } } That way, the engine pre-clones $this for me, and I operate from that. I could ignore $clone if I wanted to, but I cannot ever modify $this. (That wouldn't address the mixed class question entirely, but does address forced-immutability.) 3) A long-standing source of aggravation for user-space devs is that it's impossible to build an object that is a for-reals replacement for arrays. We can implement ArrayAccess, Countable, Iterator, whatever, but for all practical purposes they're still not even remotely close to being arrays as far as the type system or array functions are concerned. If what we're talking about here is adding more "act like a primitive" interfaces for objects, we need to think through all of those little nuances to make them as non-annoying as possible. (Eg, would an integer pass a Numberish type check? In weak mode or strict mode? Can I pass a Numberish to strpos()? Can I pass a Stringish to strpos()?) I don't have answers for those but I would hope we'd think those through to avoid the split existence that is collections/arrays.
> >>> 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. >> >> Which reinforces the point above that maybe before the are tackling the >> operators in userspace we need to identify use cases and create >> structure for them, and then it might be easier for us to avoid >> inconsistencies. We're kind of moved forward with GMP without doing >> that, but fortunately it's easier to fix such things while it's confined >> to the engine/extensions. Once it's in userspace, changing it would be >> way harder. > > In full agreement there. On that note, it's of course possible to > support overloading certain things, but only internally. It might be > reasonable to expose __assign_add (well, its C equivalent) to GMP but > not to userland, for instance. > > Regarding use cases, there are a few that come to mind. > > One of these is userland number types. We don't, to the best of my > knowledge, currently have a decimal type as a PHP extension, so > applications which need it must look to userland implementations > (often backed by bcmath underneath). These would be more pleasant and > intuitive to use if they could use our normal arithmetic operators. > For such userland types, I imagine an interface with basic arithmetic > operations would work. Perhaps we might call it \Number. A question > might be whether to split apart integer and float operations like > Haskell does. I'm not sure what fits this use-case best, though it's > probably better not to require classes to implement what they don't > need, and just let people get an error if they do, say, ($decimal % 5). > > Another use case is userland string types. There's several projects > out there which wrap PHP strings in an object to provide an > object-oriented interface which is more pleasant to use than PHP's > string API. There is also the use-case of Unicode string wrappers, > since PHP still doesn't have a Unicode string type. They would benefit > from being able to overload the concatenation operator, and possibly > also the binary operations (^, & and |) which work on strings. > Everything else these types need is already overloadable with the > existing \ArrayAccess and \Iterator(Aggregate). There is a problem > here, through, in what we should require such classes to implement > beyond concatenation. Requiring nothing else leaves it open to abuse > (I can already imagine `.` being used for the dot product), but > requiring, say, binary operations would be unfair (they're nonsense in > the context of Unicode), as would \ArrayAccess (Unicode strings may > have non-constant time indexing) or \Traversable (a Unicode string > class might choose to lack a default iterator to force the user to > consider the different ways a Unicode string can be used). Maybe we > could require \Countable, but even that has problems regarding > Unicode, non-constant time complexity especially. So, I'm not quite > sure what to do there. > > Thanks.
An example of a user-space string object is in Drupal 8: We added (very late) a TranslatableString class, which contains a string template (with placeholders), the placeholder replacements, and various cache-related metadata. Off hand, I don't know if adding a Stringish interface to that would be good or terrible. :-) It needs to be compiled into an actual string (replacing the template with an alternate version for another language if appropriate) during rendering so that we have the right cache information available, a process that requires external services. Off hand I have no idea what adding a concat() method or position()/ArrayAccess method to that would do or if it would eat our kittens, but I mention it here as another example in the wild. --Larry Garfield

Alexander Lissachenko

10 years ago
2016-01-05 2:04 GMT+03:00 Andrea Faulds <ajf@ajf.me>:
> I agree that we could do something with interfaces. I would like to point > out that we actually already have an example of this, in the form of the > \ArrayAccess interface, which requires you to implement all the different > indexing operations at once. Unfortunately, though, \ArrayAccess doesn't > give us a precedent for dealing with the $this issue (in `$a + $b`, who > gets called, how do we handle differing types, etc?), but it's a start. >
Hi, Andrea and internals team! Interface is a good way to implement new functionality for classes, but operator overloading is language feature itself, so from my point of view, it will be better to put this functionality into the magic method. Personally, I don't like hundreds of __add, __mul, __etc methods, because this will be ugly and will require addition of new methods when new operators are included (eg __pow). I want to suggest to add only one single method: public function __operator(int $operatorKind, ...$operatorArgs); This method will be called for every operator and class can check the $operatorKind variable with simple check: if (Php\Operator::OP_DIVISION === $operatorKind) return $operatorArgs[0]->value / $operatorArgs[1]->value; New values for operator type enumeration can be added later into new versions of PHP easily.

Bob Weinand

10 years ago
> Am 05.01.2016 um 15:29 schrieb Alexander Lisachenko <lisachenko.it@gmail.com>: > > 2016-01-05 2:04 GMT+03:00 Andrea Faulds <ajf@ajf.me>: > >> I agree that we could do something with interfaces. I would like to point >> out that we actually already have an example of this, in the form of the >> \ArrayAccess interface, which requires you to implement all the different >> indexing operations at once. Unfortunately, though, \ArrayAccess doesn't >> give us a precedent for dealing with the $this issue (in `$a + $b`, who >> gets called, how do we handle differing types, etc?), but it's a start. >> > > > Hi, Andrea and internals team! > > Interface is a good way to implement new functionality for classes, but > operator overloading is language feature itself, so from my point of view, > it will be better to put this functionality into the magic method. > Personally, I don't like hundreds of __add, __mul, __etc methods, because > this will be ugly and will require addition of new methods when new > operators are included (eg __pow). > > I want to suggest to add only one single method: > > public function __operator(int $operatorKind, ...$operatorArgs); > > This method will be called for every operator and class can check the > $operatorKind variable with simple check: if (Php\Operator::OP_DIVISION === > $operatorKind) return $operatorArgs[0]->value / $operatorArgs[1]->value; > New values for operator type enumeration can be added later into new > versions of PHP easily.
I think an interface is a strongly superior way, especially as it'll allow you to hint that an implementation became incomplete when an operator has been added. (Also, how often are relevant operators added?) At least as you aren't supposed to *ab*use the methods for other things than they're intended to. If you implement number operations, then you should implement the whole set of them. If you implement a string class, you should implement the array access and the string concat. Operators definitely shouldn't have specific context sensitive meaning (like printing when you bitshift (wtf, C++?)). At least, we should *assume* that the operators are used in accord with their intended meaning. Bob

Stas Malyshev

10 years ago
Hi!
> Interface is a good way to implement new functionality for classes, but > operator overloading is language feature itself, so from my point of view, > it will be better to put this functionality into the magic method.
No contradiction here. Language features can use interfaces, see Throwable or ArrayAccess.
-- Stas Malyshev smalyshev@gmail.com

Stephen Coakley

10 years ago
On Mon, 04 Jan 2016 16:34:21 +0000, Andrea Faulds wrote:
> 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 It looks like I'm late to the party for this discussion, but I feel like this is a similar concept to the already existing Comparable RFC <https:// wiki.php.net/rfc/comparable>, which in my opinion, has a much better interface. Is this slightly more in-line with what you're thinking with type classes? I'd say this RFC should address the Comparable RFC since they are in direct "conflict" (not quite the right word there). Perhaps instead group relevant operators into magic interfaces like Comparable, Arithmetic, or something along those lines. I'm in favor of operator overloading, but it really needs to be done with much care.

Andrew Faulds

10 years ago
Hi Stephen, Stephen Coakley wrote:
> > It looks like I'm late to the party for this discussion, but I feel like > this is a similar concept to the already existing Comparable RFC <https:// > wiki.php.net/rfc/comparable>, which in my opinion, has a much better > interface. Is this slightly more in-line with what you're thinking with > type classes? > > I'd say this RFC should address the Comparable RFC since they are in > direct "conflict" (not quite the right word there). Perhaps instead group > relevant operators into magic interfaces like Comparable, Arithmetic, or > something along those lines. > > I'm in favor of operator overloading, but it really needs to be done with > much care. >
Yes, the Comparable interface is the sort of thing I'm thinking about. Doing things with interfaces isn't really a terribly novel idea anyway, it's what we already do for ArrayAccess and such. Thanks.
-- Andrea Faulds https://ajf.me/

Johannes Schlueter

10 years ago
On Sat, 2016-01-02 at 18:14 -0800, Sara Golemon wrote:
> https://wiki.php.net/rfc/operator-overloading
Back in the days when I created the first implementation of operator overloading in the engine[1] I didn't see it fit for the language. Meanwhile we have more type hints and stuff making it a bit less magic. I still don't think this approach is really good. for instance we loose associativity; taking the Complex example: <?php $a = new Complex(1, 2); $b = 3; echo $a + $b; // works echo $b + $a; // will error out ?> Of course this can easily be fixed by looking at the order, at least in this simple case. In a more complex example where a function might return an integer, double or complex number this becomes more complicated. For this to work we'd need non-member functions to do this and we need function overloading ... I believe we'll only get a half-baked solution and I prefer the current state over half-baked. johannes [1] http://news.php.net/php.internals/14558

Bob Weinand

10 years ago
> Am 4.1.2016 um 20:39 schrieb Johannes Schlüter <johannes@schlueters.de>: > > On Sat, 2016-01-02 at 18:14 -0800, Sara Golemon wrote: >> https://wiki.php.net/rfc/operator-overloading > > Back in the days when I created the first implementation of operator > overloading in the engine[1] I didn't see it fit for the language. > Meanwhile we have more type hints and stuff making it a bit less magic. > I still don't think this approach is really good. for instance we loose > associativity; taking the Complex example: > > <?php > $a = new Complex(1, 2); > $b = 3; > echo $a + $b; // works > echo $b + $a; // will error out > ?> > > Of course this can easily be fixed by looking at the order, at least in > this simple case. In a more complex example where a function might > return an integer, double or complex number this becomes more > complicated. > > For this to work we'd need non-member functions to do this and we need > function overloading ... > > I believe we'll only get a half-baked solution and I prefer the current > state over half-baked. > > johannes > > [1] http://news.php.net/php.internals/14558
Then that must be very long ago, as, looking at code even as old as 5.6, http://lxr.php.net/xref/PHP_5_6/Zend/zend_operators.h#959 <http://lxr.php.net/xref/PHP_5_6/Zend/zend_operators.h#959> clearly checks both operand for object with custom do_operation. The only issue is when you try to compare two objects with each custom operation handlers, like, e.g. an instance of Complex and Fractional. To have that working, we couldn’t just use real fractionals, but would need a special ComplexFractional, as Fractional may not be aware of Complex. This really is the limit of this abstraction… But I guess, it won’t be too much of an issue in real world. Bob