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.

Reply via email to