
On Sunday 25 September 2011, 19:20:52, Chris Smith wrote:
Would it be an accurate summary of this thread that people are asking for (not including quibbles about naming and a few types):
Not quite, I'm afraid.
class Ord a => Enum a where succ :: a -> a pred :: a -> a fromEnum :: a -> Int(eger) toEnum :: Int(eger) -> a -- No instance for Float/Double
I'm not in favour of introducing an Ord constraint here. For data WeekDay = Sunday ... data Month = January ... an Ord instance would be dubious, but Enum is plenty fine.
class Ord a => Range a where rangeFromTo :: a -> a -> [a] -- subsumes Ix.range / Enum.enumFromTo rangeFromThenTo :: a -> a -> a -> [a] inRange :: (a, a) -> a -> Bool -- Does have instances for Float/Double. List ranges desugar to this. -- Also has instances for tuples
Don't mix range and arithmetic sequences. I want arithmetic sequences for Double, Float and Rational, but not range. (For Float and Double one could implement range [all values between the given bounds, in increasing order, would be the desired/expected semantics for that, I think?], but I'm rather sure that's not what one does normally want, and for Rational, you can't even implement it.) Also, I doubt whether rangeFromThenTo is a useful addition to range, I don't see how it would be natural for tuples. (The Ix instance for tuples doesn't use the lexicographic ordering, but the box-partial order - presumably so would the Range instance, so the 'distance' between two tuples would depend on the given bounds. Using the box-partial order is fine for range, but seems weird for blahFromThenTo.)
class Range a => InfiniteRange a where -- [1] rangeFrom :: a -> [a] rangeFromThen :: a -> a -> [a] -- Has instances for Float/Double -- No instances for tuples
class Range a => Ix a where index :: (a, a) -> a -> Int rangeSize :: (a, a) -> Int
-- Again no instances for Float/Double. Having an instance here implies -- that the rangeFrom* are "complete", containing all 'inRange' values
Ho Hum. So Range would continue the same ambiguity/confusion that started this thread, albeit in mitigated form. Separating range from arithmetic (or 'fixed-step-size') sequences is cleaner (we'd lose default methods anyway, you need Enum or Num && Ord for them, but we now have numericEnumFrom* to make Enum instances for Num types easier, we could move the current default methods out of the class to have enumEnumFrom* so that writing instances for Enum types would be easier).
class (RealFrac a, Floating a) => RealFloat a where ... -- existing stuff (.<.), (.<=.), (.>.), (.>=.), (.==.) :: a -> a -> Bool -- these are IEEE semantics when applicable
instance Ord Float where ... -- real Ord instance where NaN has a place
Yes. I have pondered leaving Eq and Ord for Double and Float as is and providing a newtype wrapper with container/sort-safe instances, but that'd be cumbersome, people wouldn't know they exist and (when) they have to use them, urk. Also, although it's a change in behaviour, it doesn't badly break backwards compatibility., as far as I can see (I hope x /= x isn't heavily used as a NaN test). So yes, definitely yes.
There would be the obvious properties stated for types that are instances of both Enum and Range, but this allows for non-Enum types to still be Range instances.
If there's general agreement on this, then we at least have a proposal, and one that doesn't massively complicate the existing system. The next step, I suppose would be to implement it in an AltPrelude module and (sadly, since Enum is changing meaning) a trivial GHC language extension. Then the real hard work of convincing more people to use it would start. If that succeeds, the next hard work would be finding a compatible way to make the transition...
I'm not happy with InfiniteRange, but I imagine the alternative (runtime errors) won't be popular in the present crowd.