
On Tue, Sep 20, 2011 at 3:48 PM, Chris Smith
On Tue, 2011-09-20 at 15:28 -0400, Casey McCann wrote:
I actually think the brokenness of Ord for floating point values is worse in many ways, as demonstrated by the ability to insert a value into a Data.Set.Set and have other values "disappear" from the set as a result.
Definitely Ord is worse. I'd very much like to see the Ord instance for Float and Double abandon the IEEE semantics and just put "NaN" somewhere in there -- doesn't matter where -- and provide new functions for the IEEE semantics.
It should be first, to make floating point values consistent with applying Maybe to a numeric type. Personally, I contend that the most correct solution is to distinguish between meaningful ordering relations and ones used for algorithmic convenience. As another example, the type (Integer, Integer), regarded as Cartesian coordinates, has no meaningful ordering at all but does have an obvious arbitrary total order (i.e., the current Ord instance). For purposes like implementing Data.Set.Set, we don't care at all whether the ordering used makes any particular kind of sense; we care only that it is consistent and total. For semantically-meaningful comparisons, we want the semantically-correct answer and no other. For types with no meaningful order at all, or with a meaningful total order that we can use, there is no ambiguity, but floating point values have both a semantic partial order and an obvious arbitrary total order which disagree about NaN. In the true spirit of compromise the current Ord instance fails to implement both, ensuring that things work incorrectly all the time rather than half the time. That said, in lieu of introducing multiple new type classes, note that the Haskell Report specifically describes Ord as representing a total order[0], so the current instances for floating point values seem completely indefensible. Since removing the instances entirely is probably not a popular idea, the least broken solution would be to define NaN as equal to itself and less than everything else, thus accepting the reality of Ord as the "meaningless arbitrary total order" type class I suggested above and leaving Haskell bereft of any generic semantic comparisons whatsoever. Ah, pragmatism.
As for Enum, if someone were to want a type class to represent an enumeration of all the values of a type, then such a thing is reasonable to want. Maybe you can even reasonably wish it were called Enum. But it would be the *wrong* thing to use as a desugaring for list range notation. List ranges are very unlikely to be useful or even meaningful for most such enumerations (what is [ Red, Green .. LightPurple]?); and conversely, as we've seen in this thread, list ranges *are* useful in situations where they are not a suitable way of enumerating all values of a type.
It's not clear that Enum, as it stands, actually means anything coherent at all. Consider again my example of integer (x, y) coordinates. Naively, what would [(0, 0) .. (3, 3)] appear to mean? Why, obviously it's the sixteen points whose coordinates range from 0 to 3, except it isn't because Enum isn't defined on pairs and doesn't work that way anyhow. Could we describe this range with an iteration function and a comparison? No, because the Ord instance here is intrinsically nonsensical. And yet, the intent is simple and useful, so why don't we have a standard type class for it?[1] This would seem to be the obvious, intuitive interpretation for range syntax with starting and ending values. To the extent that Enum can be given a coherent interpretation (which requires ignoring many existing instances), it seems to describe types with unary successor/predecessor operations. As such, instances for Rational, floating point values, and the like are patently nonsensical and really should be removed. An obvious generalization would be to define Enum based on an "increment" operation of some sort, in which case those instances could be defined reasonably with a default increment of 1, which is merely dubious, rather than ridiculous. The increment interpretation would be very natural for infinite lists defined with open ranges and an optional step size. Absent from the above is any interpretation of expressions like [0,2 ..11], which are ill-defined anyway, as evidenced by that expression producing lists of different lengths depending on what type is chosen for the numeric literals. Myself, I'm content to declare that use of range syntax a mistake in general, and insist that an unbounded range and something like takeWhile be used instead. - C. [0]: See here: http://www.haskell.org/onlinereport/haskell2010/haskellch6.html#x13-1290006.... [1]: Spoiler: We do.