In the Python3 spirit, it would be nice to get a type error instead of ordering by id:
$ python3 >>> 1 < 'a' Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unorderable types: int() < str() The current behavior is certainly not great, and was just defaulted because thats what Python2 does. On Sunday, December 4, 2016 at 7:39:59 PM UTC+1, Marc Mezzarobba wrote: > > Hi, > > Does Element.__richcmp__() (via CoercionModel_cache_maps.richcmp()) > really need to fall back on comparing by type/id when no common parent > is found? Since comparison operators are part of the coercion > framework, it would seem more sensible to raise an error. > > Moreover, having an essentially arbitrary return value is dangerous, > because the user can easily mistake it for a mathematical result. My > immediate reason for asking this question is an example of this kind. > I'm trying to finally remove the coercions from floating-point parents > to interval parents (as discussed here long ago). But doing so would > cause comparisons such as RIF(-1,1) < RR(2) to return nonsensical > results, while they currently give the correct answer at least when the > floating-point operand is a constant (as opposed to the result of a > floating-point computation). (Now, I can perhaps think of ad-hoc fixes > that might be enough in this particular case, but I believe the issue > is more general.) > > In contrast, the only reason I can see for this code path to exist in > the first place is that--if I understand right--Python calls > a.__richcmp__(), not a.__cmp__(), when cmp() is applied to objects a > and b whose common superclass does not implement __cmp__. Therefore, > one can end up in Element.__richcmp__() while comparing an Element to a > non-Element via cmp(). And unfortunately, there does not seem to be any > simple way to distinguish between this situation and calls to > __richcmp__() coming from comparison operators. Is this correct? Did I > miss another reason? > > If that's really the main reason, I'd argue that having cmp() fail in > some cases is not as bad as having comparison operators return nonsense > on Elements. (Besides, all cases where this could be a problem will > have to be fixed anyway to port Sage to Python3, won't they?) What do > you think? > > Note that as a middle ground, we could raise an error when coercion > fails with both operands being Elements, while still returning a result > at all costs when comparing an Element with a non-Element. However, > this wouldn't help in cases like RIF(-1, 1) < float(2). > > It would also be safe--though not ideal--to keep returning False for > comparisons using ==, and sort-of-okay to return True when comparing > with !=. And, for example, the following patch, which also whitelists > comparisons with strings (because there are typically safe and seem to > be in wide use in combinat/) only leaves us with about 40 test failures > (many of which trivial). > > @@ -1870,8 +1871,6 @@ cdef class > CoercionModel_cache_maps(CoercionModel): > > (<Element>x)._parent.get_flag(Parent_richcmp_element_without_coercion)) > return PyObject_RichCompare(x, y, op) > > - # Comparing with coercion didn't work, try something else. > - > # Try y.__richcmp__(x, revop) where revop is the reversed > # operation (<= becomes >=). > # This only makes sense when y is not a Sage Element, otherwise > @@ -1883,18 +1882,23 @@ cdef class > CoercionModel_cache_maps(CoercionModel): > if res is not NotImplemented: > return res > > - # Final attempt: compare by id() > - if (<unsigned long><PyObject*>x) >= (<unsigned > long><PyObject*>y): > - # It cannot happen that x is y, since they don't > - # have the same parent. > - return rich_to_bool(op, 1) > + if op == Py_EQ: > + return False > + elif op == Py_NE: > + return True > + elif type(y) is str: > + return rich_to_bool(op, cmp(type(x), type(y))) > else: > - return rich_to_bool(op, -1) > + raise bin_op_exception(operator_object(op), x, y) > > def _coercion_error(self, x, x_map, x_elt, y, y_map, y_elt): > """ > @@ -1924,3 +1928,18 @@ Original elements %r (parent %s) and %r > (parent %s) and maps > %s %r""" % (x_elt, y_elt, parent_c(x_elt), parent_c(y_elt), > x, parent_c(x), y, parent_c(y), > type(x_map), x_map, type(y_map), y_map)) > + > +cdef object operator_object(int op): > + if op == Py_LT: > + return operator.lt > + elif op == Py_LE: > + return operator.le > + elif op == Py_EQ: > + return operator.eq > + elif op == Py_NE: > + return operator.ne > + elif op == Py_GE: > + return operator.ge > + elif op == Py_GT: > + return operator.gt > > (By the way, does operator_object already exist somewhere?) > > Thanks for your input, > > -- > Marc > > -- You received this message because you are subscribed to the Google Groups "sage-devel" group. To unsubscribe from this group and stop receiving emails from it, send an email to sage-devel+unsubscr...@googlegroups.com. To post to this group, send email to sage-devel@googlegroups.com. Visit this group at https://groups.google.com/group/sage-devel. For more options, visit https://groups.google.com/d/optout.