Minor engine patch to facilitate PECL/operator

php.internals

Sara Golemon

20 years ago
Hi, purely selfish request here, I'd like to make a minor change to the parser... Just a bitty one... You won't even feel it... Currently, when the parser encounters: expr1 > expr2 it quietly (and reasonably) switches it around to expr2 < expr1 so that is can reuse the ZEND_IS_SMALLER op. The executor has no idea this has occured, but heck, why does it need to know after all? Enter PECL/operator, which tries to implement operator overloading for objects (and does a decent job at that). In the interrest of ease of use and consistency, every overloaded binary operator is [meant to be] left associative. This is, in the expression expr1 op expr2 expr1 gets to decide how it will combine-with/compare-to expr2. Since greater-than (and greater-than-or-equal-to) are quietly flipped, PECL/operator has no way to tell the difference between the two and has to assume op1 < op2 (when in fact op2 > op1 may be the correct view). To solve this I've bundled a patch with PECL/operator to set the otherwise unused extended_value to a 0/1 value depending on whether it's genuinely less-than or greater-than. There are sleaker ways to do this than what's included in the patch (e.g. a new zend_do_() method really isn't needed), but I was attempting to touch as little as possible in implementing it. Is there a chance of this getting merged into the engine? Or is that just entirely too imposing for such an edge case extension. http://cvs.php.net/viewcvs.cgi/pecl/operator/compare-greater-5.1.2.diff?view=markup&rev=1.2 -Sara

Zeev Suraski

20 years ago
At 19:23 16/02/2006, Sara Golemon wrote:
>Hi, purely selfish request here, I'd like to make a minor change to >the parser... Just a bitty one... You won't even feel it... > >Currently, when the parser encounters: expr1 > expr2 it quietly >(and reasonably) switches it around to expr2 < expr1 so that is can >reuse the ZEND_IS_SMALLER op. The executor has no idea this has >occured, but heck, why does it need to know after all? > >Enter PECL/operator, which tries to implement operator overloading >for objects (and does a decent job at that). In the interrest of >ease of use and consistency, every overloaded binary operator is >[meant to be] left associative. This is, in the expression expr1 >op expr2 expr1 gets to decide how it will combine-with/compare-to expr2.
What does it mean that it gets to decide? If it's left associative, then it's evaluated from left to right, no?
>Since greater-than (and greater-than-or-equal-to) are quietly >flipped, PECL/operator has no way to tell the difference between the >two and has to assume op1 < op2 (when in fact op2 > op1 may be the >correct view). To solve this I've bundled a patch with >PECL/operator to set the otherwise unused extended_value to a 0/1 >value depending on whether it's genuinely less-than or greater-than. > >There are sleaker ways to do this than what's included in the patch >(e.g. a new zend_do_() method really isn't needed), but I was >attempting to touch as little as possible in implementing it. > >Is there a chance of this getting merged into the engine? Or is >that just entirely too imposing for such an edge case extension. > >http://cvs.php.net/viewcvs.cgi/pecl/operator/compare-greater-5.1.2.diff?view=markup&rev=1.2
The patch looks harmless enough but I'm not clear on why it's needed, which makes me worry whether there's some 'magic' involved here... Can you try to elaborate on what would be going wrong w/o it? Zeev

Sara Golemon

20 years ago
> >Enter PECL/operator, which tries to implement operator overloading > >for objects (and does a decent job at that). In the interrest of > >ease of use and consistency, every overloaded binary operator is > >[meant to be] left associative. This is, in the expression expr1 > >op expr2 expr1 gets to decide how it will combine-with/compare-to
expr2.
> > > What does it mean that it gets to decide? If it's left associative, > then it's evaluated from left to right, no? >
Yes, and that's the problem. $a > $b *isn't* read by the current parser as $a > $b, it's read as $b < $a. For all normal PHP comparisons, the distinction is unimportant... 4 < 2 and 2 > 4 will both evaluate to false after all. However, the way object operator overloading is being done is: If the first operand is an object implementing an overload method for this op, dispatch to that method rather than using the tranditional comparison function. e.g. $a < $b turns into $a->__is_smaller($b). Now say you've got that expression the other way around: $a > $b. What you'd expect is that if $a is an object, it'll try to call $a->__is_greater($b). What you get is an automatic reversal of the expression by the engine: $b < $a, which turns into: "If $b is an object, dispatch to $b->__is_smaller($a);" which is not the same thing. -Sara

Andi Gutmans

20 years ago
<DISCLAIMER> I am not a big fan of operator overloading because after years of C++ development I came to the conclusion that the syntactic sugar it provided was not worth the debugging hours lost due to hidden magic happening. It actually makes it hard to understand certain code when you're just reading it then explicitly calling the methods like in Java, something like obj.equals(2). </DISCLAIMER> Anyway, I think this case has some other issues. How would you treat 2 < $obj. Wouldn't you want to call $obj->__is_greater(2)? Actually my suggestion would be to not make it left associative but to require a consistent design. If you have $obj1 < $obj2 then I don't think it has to be defined which one gets called or whether __is_smaller or __is_greater is called. If either of these are overloaded, it should know how to deal with objects that can compare with it (possibly via interface, etc..) and with objects that it can't compare to (possibly always returning false, etc...). So I suggest to create the following behavior: $obj < 2 - Calls $obj->__is_smaller(2); $obj > 2 - Calls $obj->__is_greater(2); (figures our that 2 isn't overloaded so checks $obj) $obj1 > $obj2 - Can call either depending on which one implements the overloading. In any case, if at all, I think the right thing would be to have an IS_GREATER opcode and not an extended_value hack. But as I said, I don't think operator overloading should be part of standard PHP so I think making such changes does not make immediate sense. We could possibly split that into two opcodes for PHP 6 mainly as a "documentation" feature of the source code so less is lost during the compile. Andi At 09:55 AM 2/16/2006, Sara Golemon wrote:

Sara Golemon

20 years ago
> <DISCLAIMER> > I am not a big fan of operator overloading because after years of C++ > development I came to the conclusion that the syntactic sugar it > provided was not worth the debugging hours lost due to hidden magic > happening. It actually makes it hard to understand certain code when > you're just reading it then explicitly calling the methods like in > Java, something like obj.equals(2). > </DISCLAIMER> >
<MYDISCLAIMER> Believe it or not, I agree with you on the readability problems inherent in overloading. I'm not by any measure asking for a blessing or even a whiff of a consideration of putting this kind of functionality in the language. It's a decidedly non-PHP feature and one which can stay happily in extension land where it'll avoid creaping into mainstream projects, while still existing as a usable tool for those who want it. Of course, like all good knives, it does have its uses... </MYDISCLAIMER>
> Anyway, I think this case has some other issues. How would you treat > 2 < $obj. Wouldn't you want to call $obj->__is_greater(2)? Actually > my suggestion would be to not make it left associative but to require > a consistent design. If you have $obj1 < $obj2 then I don't think it > has to be defined which one gets called or whether __is_smaller or > __is_greater is called. If either of these are overloaded, it should > know how to deal with objects that can compare with it (possibly via > interface, etc..) and with objects that it can't compare to (possibly > always returning false, etc...). > So I suggest to create the following behavior: > $obj < 2 - Calls $obj->__is_smaller(2); > $obj > 2 - Calls $obj->__is_greater(2); (figures our that 2 isn't > overloaded so checks $obj) > $obj1 > $obj2 - Can call either depending on which one implements the > overloading. >
I gave this some thought as I was designing the overloading approach. I didn't go there because I actually wanted to allow asymmetry between the operands as sometimes communative properties don't apply cleanly. Most obviously in the case of sub/div ops, but also in certain other (less likely) spots like matrix maths. a[1,3] * b[3,1] is certainly different than b[3,1] * a[1,3]. I'll take a look at applying this to the is_smaller op at least. I'm not a master mathematician, but I'm fairly certain that 2 is always greater than 1.(Cue the cue the internals@ response to the contrary)
> We could possibly split that into two opcodes for PHP 6 mainly as a > "documentation" feature of the source code so less is lost during the
compile.
>
That would certainly address PECL/operator's needs and only costs the engine in the form of a slightly increased binary (which the extval hack does anyway). -Sara

Andi Gutmans

20 years ago
At 02:39 PM 2/16/2006, Sara Golemon wrote:
>I gave this some thought as I was designing the overloading approach. I >didn't go there because I actually wanted to allow asymmetry between the >operands as sometimes communative properties don't apply cleanly. Most >obviously in the case of sub/div ops, but also in certain other (less >likely) spots like matrix maths. a[1,3] * b[3,1] is certainly different than >b[3,1] * a[1,3].
True.
>I'll take a look at applying this to the is_smaller op at least. I'm not a >master mathematician, but I'm fairly certain that 2 is always greater than >1.(Cue the cue the internals@ response to the contrary) > > We could possibly split that into two opcodes for PHP 6 mainly as a > > "documentation" feature of the source code so less is lost during the >compile. > > >That would certainly address PECL/operator's needs and only costs the engine >in the form of a slightly increased binary (which the extval hack does >anyway).
Yeah, well let's see if we can find some other compelling reasons to possible make the change for PHP 6. In the meantime, I think providing the patch with your extension should be OK. A hack for a hack :) Andi

Zeev Suraski

20 years ago
At 00:39 17/02/2006, Sara Golemon wrote:
>I gave this some thought as I was designing the overloading approach. I >didn't go there because I actually wanted to allow asymmetry between the >operands as sometimes communative properties don't apply cleanly. Most >obviously in the case of sub/div ops, but also in certain other (less >likely) spots like matrix maths. a[1,3] * b[3,1] is certainly different than >b[3,1] * a[1,3]. > >I'll take a look at applying this to the is_smaller op at least. I'm not a >master mathematician, but I'm fairly certain that 2 is always greater than >1.(Cue the cue the internals@ response to the contrary)
I agree with that 'theory', I tend to think that relation operators fall in a different category from operation operators (or whatever they're called :) x>y is pretty much by definition the same as y<x, so I don't think we need to allow for different implementations. I think you need to reconsider allowing two different object types to be compared (hence opening the possibility for inconsistent results, e.g. both x>y and x<y evaluating to true). The rest of the operators should be compatible with non-commutative implementations. Zeev

Zeev Suraski

20 years ago
I understand. Aren't you slightly worried about the implications of making it possible for x>y to have a different meaning than y<x? In languages where operator overloading is supported, it comes hand in hand with strict typing, which wouldn't allow for different values for x>y and y<x... Zeev At 19:55 16/02/2006, Sara Golemon wrote:

Benj Carson

20 years ago
Just thought I'd chime in here with an example that seemed relevant: cout << "foo" << "bar" << endl; cin >> input; Here, << and >> have nothing to do with bit-shifting and have different meanings. So, maybe one can't assume that '<' and '>' always have to apply to the typical mathematical operations (e.g. $pacman < $ghost). Benj Carson PS: Is it a coincidence that goto support & operator overloading have the same author? ;) On Thursday 16 February 2006 15:55, Zeev Suraski wrote:

Sara Golemon

20 years ago
> Here, << and >> have nothing to do with bit-shifting and have different > meanings. So, maybe one can't assume that '<' and '>' always have to
apply
> to the typical mathematical operations (e.g. $pacman < $ghost). >
That's one reason (one which lends credibility to the overloading kills readability argument).
> PS: Is it a coincidence that goto support & operator overloading have the > same author? ;) >
They say idle hands are the devil's playthings.... My hands are very idle....

Stefan Walk

20 years ago
On 16/02/06, Zeev Suraski <zeev@zend.com> wrote:
> In languages where operator overloading is supported, it comes hand > in hand with strict typing, which wouldn't allow for different values > for x>y and y<x... > > Zeev
That's not true, Ruby for example has operator overloading, and has no problems with different meanings of x>y and y<x (but i don't know a core class that does that). Also, PHP already breaks the transitivity rule for the equality operator ($a == $b and $b == $c does not imply $a == $c), so there's not much new evil if a user can, by loading an extension, break the symmetry of the comparison operators, IMO. Regards, Stefan Walk

Zeev Suraski

20 years ago
At 11:55 17/02/2006, Stefan Walk wrote:
>On 16/02/06, Zeev Suraski <zeev@zend.com> wrote: > > In languages where operator overloading is supported, it comes hand > > in hand with strict typing, which wouldn't allow for different values > > for x>y and y<x... > > > > Zeev > >That's not true, Ruby for example has operator overloading, and has no >problems with different meanings of x>y and y<x (but i don't know a >core class that does that).
I mean *real* languages :) Seriously though, it sounds like a bad idea to allow it.
>Also, PHP already breaks the transitivity rule for the equality >operator ($a == $b and $b == $c does not imply $a == $c), so there's >not much new evil if a user can, by loading an extension, break the >symmetry of the comparison operators, IMO.
Oh I disagree. While we do have some unique cases in which transitivity is not maintained, they are quite, well, unique, and arguably make sense. More importantly, the discussion here is not about transitivity (although it does have transitivity implications). It's more fundamental, it's about the very meaning of smaller-than / greater-than and the relationship between them. Zeev

Marcus Börger

20 years ago
Hello Zeev, Friday, February 17, 2006, 12:09:26 PM, you wrote:
> At 11:55 17/02/2006, Stefan Walk wrote: >>On 16/02/06, Zeev Suraski <zeev@zend.com> wrote: >> > In languages where operator overloading is supported, it comes hand >> > in hand with strict typing, which wouldn't allow for different values >> > for x>y and y<x... >> > >> > Zeev >> >>That's not true, Ruby for example has operator overloading, and has no >>problems with different meanings of x>y and y<x (but i don't know a >>core class that does that).
> I mean *real* languages :) Seriously though, it sounds like a bad > idea to allow it.
>>Also, PHP already breaks the transitivity rule for the equality >>operator ($a == $b and $b == $c does not imply $a == $c), so there's >>not much new evil if a user can, by loading an extension, break the >>symmetry of the comparison operators, IMO.
> Oh I disagree. While we do have some unique cases in which > transitivity is not maintained, they are quite, well, unique, and > arguably make sense. > More importantly, the discussion here is not about transitivity > (although it does have transitivity implications). It's more > fundamental, it's about the very meaning of smaller-than / > greater-than and the relationship between them.
Actually Sara only asked whether she could add a flag to some handlers. That has nothing to with anything you started to discuss from there. Best regards, Marcus

Zeev Suraski

20 years ago
At 01:24 18/02/2006, Marcus Boerger wrote:
>Actually Sara only asked whether she could add a flag to some handlers. >That has nothing to with anything you started to discuss from there.
Uhm, Sara asked whether she could add a flag to a specific operator to facilitate a certain behavior, namely, the ability to make x<y and y>x be treated differently. It has everything to do with what I discussed from there (but not with transitivity). Zeev

Marcus Börger

20 years ago
Hello Zeev and hello Sara Saturday, February 18, 2006, 1:04:20 AM, you wrote:
> At 01:24 18/02/2006, Marcus Boerger wrote: >>Actually Sara only asked whether she could add a flag to some handlers. >>That has nothing to with anything you started to discuss from there.
> Uhm, Sara asked whether she could add a flag to a specific operator > to facilitate a certain behavior, namely, the ability to make x<y and
y>>x be treated differently. It has everything to do with what I
> discussed from there (but not with transitivity).
the point is not being able to treat them differently. The point is being able to distinguish them so the right operation is called. Btw, in c++ it is common to do things like: bool operator != (_IN Type& oth) { return !(operator == (oth)); } for < and > it means in php the user could (should we add overloading) do function __less_then($other) { return $other->__greater($this); } But before you think i want op overloading in php let me assure you that i do not want that. The reson is that it is the opposite of our beloved KISS approach. So for example sooner or later we'd reach a point where crazy stuff - really crazy stuff - nobody understands is needed. For example another view on C++. Most overloaders do checking for "has some value" or "is valid" by: operator bool () const {... } Which leads to a lot of problems. While the correct version would be: typedef MemberType ClassType::*unspecified_boolean_type; operator ClassType::*unspecified_boolean_type() { return valid ? &ClassType::member : 0; } MemberType member; And now tell me how many ppl do you know who understand the above and the implications the simple bool conversion has. Best regards, Marcus

Zeev Suraski

20 years ago
At 03:02 18/02/2006, Marcus Boerger wrote:
> the point is not being able to treat them differently. The point is being >able to distinguish them so the right operation is called.
Erm sorry, but it's exactly the same :) Zeev

Jakub Vrana

20 years ago
Sara Golemon wrote:
> Yes, and that's the problem. $a > $b *isn't* read by the current parser as > $a > $b, it's read as $b < $a.
> For all normal PHP comparisons, the distinction is unimportant... 4 < 2 > and 2 > 4 will both evaluate to false after all.
Array comparison is due this logic also strange: $a = array(0, 1); $b = array(1 => 0, 0 => 1); var_dump($a < $b); // true var_dump($a > $b); // true $a is both less than and greater than $b. Jakub Vrana

Jochem Maas

20 years ago
Jakub Vrana wrote:
> Sara Golemon wrote:
with thanks to Sara! - because (not for the first time) your post allowed me to learn/understand some new stuff. (Sara seems to have a knack for explaining stuff in a way that dummies can [also] understand it - that a good thing[tm]!)
> >>Yes, and that's the problem. $a > $b *isn't* read by the current parser as >>$a > $b, it's read as $b < $a. > > >>For all normal PHP comparisons, the distinction is unimportant... 4 < 2 >>and 2 > 4 will both evaluate to false after all. > > > Array comparison is due this logic also strange: > > $a = array(0, 1); > $b = array(1 => 0, 0 => 1);
I started playing with this because I didn't understand it. I could only make both the LT and the GT expressions equal true with these particular arrays. for instance if I changed them to: $a = array(0, 1); $b = array(0 => 1, 1 => 0); then the var_dump() statements give me: bool(true) bool(false) Personally I would never use a statement like ($a < $b) if $a and $b are arrays - mainly because I have no idea what that really means (array $a being 'less than' $b) so the determining whether $a is 'less than' $b is not much use. given that fact I'm not fussed whether array comparison (in such a way) is broken or not ... but I am very interested to here 'core people's advice on using this kind of expression syntax with arrays (I feel objects have the same problem as arrays with regard to what 'greater than'/'less than' really means when comparing to object variables).

Zeev Suraski

20 years ago
Interesting stuff! Exactly the kind of stuff that's worrying me. I think you found a conceptual bug in zend_hash_compare(), I think it's worth fixing. Zeev At 14:59 17/02/2006, Jakub Vrana wrote: