Re: [Python-Dev] PEP 207 -- Rich Comparisons

Guido van Rossum writes:
You have to choose between using rich comparisons or boolean comparisons. You can't use both for the same (rich/complex) object.
No. I think NumPy is the tip of the iceberg, when discussing new semantics. Most users don't consider these broader semantic issues, because Python doesn't give them the opportunity to do so. I can see possible scenarios of using both boolean and non-boolean comparisons for Python lists and dictionaries in addition to NumPy. I chose to use Python because it provides a richer framework than other languages. When Python fails to provide such benefits, I'll move to another language. I moved from PERL to Python because the multi-dimensional array syntax is vastly better in Python than PERL, though as a novice I don't have to know that it exists. What I'm proposing here is in a similar vein.
Yes, I would hope so! It appears though that you misunderstand me. My point was that I tend to agree with Jim Fulton's arguments for a limited interpretation of the current comparison operators. I too expect them to return a boolean result. I have never felt comfortable using such comparison operators in an array context, e.g. as in the array language, IDL. It just looks wrong. So my suggestion is to create new ones whose implicit meaning is to provide element-wise or rich comparison behavior. And to add similar behavior for the other operators for consistency. Can someone provide an example in mathematics where comparison operators are used in a non-boolean, ie. rich comparison, context. If so, this might shut me up!
Phrase should be: "but I believe without making it more complex.". -------
The last phrase should read: "while not greatly impacting current --- Python behavior."
No, I do understand. I've read most of the early discussions on this issue and one of those issues was about having to choose between boolean and rich comparisons and what should take precedence, when both may be appropriate. I'm suggesting an alternative here.
Yes, I understand.
Red my lips: there won't be *any* new operators in 2.1.
OK, I didn't expect this to make it into 2.1.
Yes, I agree, this should be done anyway. I'm just not sure that the implicit meaning that these comparison operators are being given is the best one. I'm just looking for ways to incorporate rich comparisons into a broader framework, numpy just currently happens to be the primary example of this proposal. Assuming the current comparison operator overloading is already implemented and has been used to implement rich comparisons for some objects, then my rich comparison proposal would cause confusion. This is what I'm trying to avoid.
So! Introductory books don't have to discuss these additional operators. I don't have to know about XML and socket modules to start using Python effectively, nor do I have to know about 'zip' or list comprehensions. These additions decrease the code size and increase efficiency, but don't really add any new expressive power that can't already be done by a 'for' loop. I'll try to convince myself that this suggestion is crazy and not bother you with this issue for awhile. Cheers, Paul

Paul Barret:
1. The current boolean operator behavior does not have to change, and hence will be backward compatible.
Guido van Rossum:
What incompatibility do you see in the current proposal?
Paul Barret:
You have to choose between using rich comparisons or boolean comparisons. You can't use both for the same (rich/complex) object.
Sure. I thought that the NumPy folks were happy with this. Certainly two years ago they seemed to be.
That's the same argument that has been made for new operators all along. I've explained already why they are not on the table for 2.1.
Not me (I no longer consider myself a mathematician :-). Why are you requiring an example from math though? Again, you will be able to make this argument to the NumPy folks when they are ready to change the meaning of A<B to mean an array of Booleans rather than a single Boolean. Since you're part of the design team for NumPy NG, you're in a pretty good position to hold out for elementwise operators! However, what would you do if elementwise operators were turned down for ever (which is a realistic possibility)? In the mean time, I see no harm in *allowing* the comparison operators to be overridden to return something else besides a Boolean. Someone else may find this useful. (Note that standard types won't use this new freedom, so I'm not imposing this on anybody -- I'm only giving a new option.)
I don't see any argument for elementwise operators here that I haven't heard before, and AFAIK it's all in the two PEPs.
Note that Python doesn't decide which should take precedent. The implementer of an individual extension type decides what his comparison operators will return.
AFAIK, rich comparisons haven't been used anywhere to return non-Boolean results.
Happy holidays nevertheless. :-) --Guido van Rossum (home page: http://www.python.org/~guido/)

[Paul Barrett]
By my informal accounting, over the years there have been more requests for three-outcome comparison operators than for elementwise ones, although the three-outcome lobby isn't organized so is less visible. It's a natural request for anyone working with partial orderings (a < b -> one of {yes, no, unordered}). Another large group of requests comes from people working with variants of fuzzy logic, where it's desired that the comparison operators be definable to return floats (intuitively corresponding to the probability that the stated relation "is true"). Another desire comes from the symbolic math camp, which would like to be able to-- as is possible for "+", "*", etc --define "<" so that e.g. "x < y" return an object capturing that somebody *asked* for "x < y"; they're not interested in numeric or Boolean results so much as symbolic expressions. "<" is used for all these things in the literature too. Whatever. "<" and friends are just collections of pixels. Add 300 new operator symbols, and people will want to redefine all of them at will too. draw-a-line-in-the-sand-and-the-wind-blows-it-away-ly y'rs - tim

[Paul Barrett]
Not exactly mathematical, but some day I'd like to create a database access module which lets you say things like mydb = OpenDB("inventory") parts = mydb.parts tuples = mydb.retrieve(parts.name, parts.number).where(parts.quantity >= 42) Of course, to really make this work I need to be able to overload "and" and "or" as well, but that's a whole 'nother PEP... Greg Ewing, Computer Science Dept, +--------------------------------------+ University of Canterbury, | A citizen of NewZealandCorp, a | Christchurch, New Zealand | wholly-owned subsidiary of USA Inc. | greg@cosc.canterbury.ac.nz +--------------------------------------+

Believe it or not, in 1998 we already had a suggestion for overloading these too. This is hinted at in David Ascher's proposal (the Appendix of PEP 208) where objects could define __boolean_and__ to overload x<y<z. It doesn't get all the details right: it's not enough to check if the left operand is true, since that leaves 'or' out in the cold, but a different test (i.e. the presence of __boolean_and__) would work. I am leaving this out of the current PEP because the bytecode you have to generate for this is very hairy. A simple expression like ``f() and g()'' would become something like: outcome = f() if hasattr(outcome, '__boolean_and__'): outcome = outcome.__boolean_and__(g()) elif outcome: outcome = g() The problem I have with this is that the code to evaluate g() has to be generated twice! In general, g() could be an arbitrary expression. We can't evaluate g() ahead of time, because it should not be evaluated at all when outcome is false and doesn't define __boolean_and__(). For the same reason the current PEP doesn't support x<y<z when x<y doesn't return a Boolean result; a similar solution would be possible. --Guido van Rossum (home page: http://www.python.org/~guido/)

The problem I have with this is that the code to evaluate g() has to be generated twice!
I have an idea how to fix that. There need to be two methods, __boolean_and_1__ and __boolean_and_2__. The first operand is evaluated and passed to __boolean_and_1__. If it returns a result, that becomes the result of the expression, and the second operand is short-circuited. If __boolean_and_1__ raises a NeedOtherOperand exception (or there is no __boolean_and_1__ method), the second operand is evaluated, and both operands are passed to __boolean_and_2__. The bytecode would look something like <evaluate first operand> BOOLEAN_AND_1 label <evaluate second operand> BOOLEAN_AND_2 label: ... I'll make a PEP out of this one day if I get enthusiastic enough. Greg Ewing, Computer Science Dept, +--------------------------------------+ University of Canterbury, | A citizen of NewZealandCorp, a | Christchurch, New Zealand | wholly-owned subsidiary of USA Inc. | greg@cosc.canterbury.ac.nz +--------------------------------------+

Paul Barret:
1. The current boolean operator behavior does not have to change, and hence will be backward compatible.
Guido van Rossum:
What incompatibility do you see in the current proposal?
Paul Barret:
You have to choose between using rich comparisons or boolean comparisons. You can't use both for the same (rich/complex) object.
Sure. I thought that the NumPy folks were happy with this. Certainly two years ago they seemed to be.
That's the same argument that has been made for new operators all along. I've explained already why they are not on the table for 2.1.
Not me (I no longer consider myself a mathematician :-). Why are you requiring an example from math though? Again, you will be able to make this argument to the NumPy folks when they are ready to change the meaning of A<B to mean an array of Booleans rather than a single Boolean. Since you're part of the design team for NumPy NG, you're in a pretty good position to hold out for elementwise operators! However, what would you do if elementwise operators were turned down for ever (which is a realistic possibility)? In the mean time, I see no harm in *allowing* the comparison operators to be overridden to return something else besides a Boolean. Someone else may find this useful. (Note that standard types won't use this new freedom, so I'm not imposing this on anybody -- I'm only giving a new option.)
I don't see any argument for elementwise operators here that I haven't heard before, and AFAIK it's all in the two PEPs.
Note that Python doesn't decide which should take precedent. The implementer of an individual extension type decides what his comparison operators will return.
AFAIK, rich comparisons haven't been used anywhere to return non-Boolean results.
Happy holidays nevertheless. :-) --Guido van Rossum (home page: http://www.python.org/~guido/)

[Paul Barrett]
By my informal accounting, over the years there have been more requests for three-outcome comparison operators than for elementwise ones, although the three-outcome lobby isn't organized so is less visible. It's a natural request for anyone working with partial orderings (a < b -> one of {yes, no, unordered}). Another large group of requests comes from people working with variants of fuzzy logic, where it's desired that the comparison operators be definable to return floats (intuitively corresponding to the probability that the stated relation "is true"). Another desire comes from the symbolic math camp, which would like to be able to-- as is possible for "+", "*", etc --define "<" so that e.g. "x < y" return an object capturing that somebody *asked* for "x < y"; they're not interested in numeric or Boolean results so much as symbolic expressions. "<" is used for all these things in the literature too. Whatever. "<" and friends are just collections of pixels. Add 300 new operator symbols, and people will want to redefine all of them at will too. draw-a-line-in-the-sand-and-the-wind-blows-it-away-ly y'rs - tim

[Paul Barrett]
Not exactly mathematical, but some day I'd like to create a database access module which lets you say things like mydb = OpenDB("inventory") parts = mydb.parts tuples = mydb.retrieve(parts.name, parts.number).where(parts.quantity >= 42) Of course, to really make this work I need to be able to overload "and" and "or" as well, but that's a whole 'nother PEP... Greg Ewing, Computer Science Dept, +--------------------------------------+ University of Canterbury, | A citizen of NewZealandCorp, a | Christchurch, New Zealand | wholly-owned subsidiary of USA Inc. | greg@cosc.canterbury.ac.nz +--------------------------------------+

Believe it or not, in 1998 we already had a suggestion for overloading these too. This is hinted at in David Ascher's proposal (the Appendix of PEP 208) where objects could define __boolean_and__ to overload x<y<z. It doesn't get all the details right: it's not enough to check if the left operand is true, since that leaves 'or' out in the cold, but a different test (i.e. the presence of __boolean_and__) would work. I am leaving this out of the current PEP because the bytecode you have to generate for this is very hairy. A simple expression like ``f() and g()'' would become something like: outcome = f() if hasattr(outcome, '__boolean_and__'): outcome = outcome.__boolean_and__(g()) elif outcome: outcome = g() The problem I have with this is that the code to evaluate g() has to be generated twice! In general, g() could be an arbitrary expression. We can't evaluate g() ahead of time, because it should not be evaluated at all when outcome is false and doesn't define __boolean_and__(). For the same reason the current PEP doesn't support x<y<z when x<y doesn't return a Boolean result; a similar solution would be possible. --Guido van Rossum (home page: http://www.python.org/~guido/)

The problem I have with this is that the code to evaluate g() has to be generated twice!
I have an idea how to fix that. There need to be two methods, __boolean_and_1__ and __boolean_and_2__. The first operand is evaluated and passed to __boolean_and_1__. If it returns a result, that becomes the result of the expression, and the second operand is short-circuited. If __boolean_and_1__ raises a NeedOtherOperand exception (or there is no __boolean_and_1__ method), the second operand is evaluated, and both operands are passed to __boolean_and_2__. The bytecode would look something like <evaluate first operand> BOOLEAN_AND_1 label <evaluate second operand> BOOLEAN_AND_2 label: ... I'll make a PEP out of this one day if I get enthusiastic enough. Greg Ewing, Computer Science Dept, +--------------------------------------+ University of Canterbury, | A citizen of NewZealandCorp, a | Christchurch, New Zealand | wholly-owned subsidiary of USA Inc. | greg@cosc.canterbury.ac.nz +--------------------------------------+
participants (4)
-
Greg Ewing
-
Guido van Rossum
-
Paul Barrett
-
Tim Peters