Folks The concrete is setting fast, but Ross points out that the instance for Enum (Ratio a) is inconsistent with that for Enum Float and Enum Double. (It's strange that these non-integral types are in Enum, but we're stuck with that.) All three use 'numericEnumFrom' etc for the enumFrom method, but Float and Double define succ and pred thus: instance Enum Float where succ x = x+1 pred x = x-1 But Ratio does not... it uses the default method for succ/pred which gives instance Integral a => Enum (Ratio a) where succ x = fromInt (1 + (toInt x)) Thus (succ (1.5 :: Float)) is 2.5 but (succ (3%2)) is 2%1 Furthermore, you'd expect that [x..y] would mean the same as [x,succ x .. y] and that is true for Float/Double but not for Ratio. So I propose to modify the instance decl for Ratio by adding explicit defns for succ/pred just like those in Float/Double. Any objections? Simon PS: CUP are typesetting the report now, assuming we can come to happy agreement about copyright etc. On that front, the CUP wheels are grinding, but they grind slow. I'll emit an update when I know something useful.
"Simon Peyton-Jones" <simonpj@microsoft.com> writes:
So I propose to modify the instance decl for Ratio by adding explicit defns for succ/pred just like those in Float/Double.
I bet you guessed: once at it, what about removing those unintuitive 1/2-s, like: numericEnumFromTo n m = takeWhile (<= m) (numericEnumFrom n) numericEnumFromThenTo n n' m = takeWhile p (numericEnumFromThen n n') where p | n' > n = (<= m + (n'-n)) | otherwise = (>= m + (n'-n)) Everybody working with floats should know their quirks, while Rationals should be treated exactly. Feri.
Ferenc Wagner wrote:
"Simon Peyton-Jones" <simonpj@microsoft.com> writes:
So I propose to modify the instance decl for Ratio by adding explicit defns for succ/pred just like those in Float/Double.
I bet you guessed: once at it, what about removing those unintuitive 1/2-s, like:
numericEnumFromTo n m = takeWhile (<= m) (numericEnumFrom n) numericEnumFromThenTo n n' m = takeWhile p (numericEnumFromThen n n') where p | n' > n = (<= m + (n'-n)) | otherwise = (>= m + (n'-n))
Everybody working with floats should know their quirks, while Rationals should be treated exactly.
It's inconsistent to remove the "+1/2" for numericEnumFromTo but to leave the "+(n'-n)" for numericEnumFromThenTo. I think you probably mean to remove both (actually, all three). I wouldn't recommend these changes for Float and Double (the increments are there to account for arithmetic inexactness), but they do make a lot of sense to me for (Ratio a), which is exact. Is is too late to consider such a change? -- Dean
Dean Herington <heringto@cs.unc.edu> writes:
It's inconsistent to remove the "+1/2" for numericEnumFromTo but to leave the "+(n'-n)" for numericEnumFromThenTo. I think you probably mean to remove both (actually, all three).
You are right, what I meant is numericEnumFromTo n m = takeWhile (<= m) (numericEnumFrom n) numericEnumFromThenTo n n' m = takeWhile p (numericEnumFromThen n n') where p | n' >= n = (<= m) | otherwise = (>= m)
I wouldn't recommend these changes for Float and Double (the increments are there to account for arithmetic inexactness)
That's clear, but see below.
(1) In section A (Standard Prelude), in the definition of `numericEnumFromThenTo`, change "n' > n" to "n' >= n", to agree with the last bullet in section 6.3.4.
Good point, also included in the above code. George Russell <ger@tzi.de> writes:
Indeed I think the Haskell Library Report contains quite a few examples of floating point code which a numerical analyst would have written rather better. So I don't think it's good enough to treat every Float/Double operation as if it had an implicit "UNSAFE" flag indicating that the compiler was entitled to weird behaviour only comprehensible to experts, as seems to be the case with floating enumerations now.
Simon made clear that he wants to change as little as possible. This area is inconsistent as a whole, and I agree that the best solution would be to do away with the 'impossible' stuff at least (Float and Double): then every user could define her Enum instance in the most appropriate way, fuzzing with +epsilon or *(1+epsilon) or whatever. Or simply doesn't use the nice syntax. In my opinion getting elements MUCH greater than the upper limit is more confusing than losing the last one, which is a familiar and well known consequence of finite precision. And the above MUCH is very hard to tell. Just think of [1%1..2], [1%2..2], [1%3..2], etc. Which sequence is the first with three elements? Currently the second. For Floats my above argument is even less convincing. It's impossible to do it right, but apparently we have to do something. I vote for less magic (MUCH=0), so that the resulting weirdness be comprehensible to more experts, not only to the ones who know the language definition by heart. Feri.
Hi Haskellers, Simon> Any objections? frankly speaking, yes. The intellectual meaning of a successor in a non-integral type is not clear for me. Can anyone explain it? I observed this report discussion for quite a long time and maybe have found the deep reason behind the problems. It appears to me that people like to add flexibility to the language that should better not be added since it destroys structure. The software engineering power of Haskell mainly comes from its structure. One is surely more flexible with an untyped, not referentially transparent language, but many efforts have been made to add flexibility without loss of structure, e.g., Generic Haskell. Surely, one can enumerate the rationals like one can enumerate pairs of integers, but that is not the kind of enumeration discussed here, which is suppose to have a constant increment/decrement. Unfortunately, several decisions concerning the predefined type class system have already been kept according to other programming languages instead of algebra. The latter would have provided both mathematical rigor and more possibilities for useful overloading of predefined operators. Making non-integrals an instance of Enum appears to me like a dirty hack, to reuse nice syntax. Maybe, it would be more genuine to say that [a..b] is a counterpart of a loop and use a new class called Iterator for a and b. In any case, numbers which do not permit an exact increment, as is the case for Float and Double should not be permitted as types of iteration variables, since it would legitimate bad programming style. Since I suppose you also want to receive constructive suggestions, I'd suggest to remove some instance definitions from the report. Would any dubious instance definition have a consequence on the performance of compiled code if it is, for compatibility, only provided by a kind of deprecated prelude or library? Cheers -- Christoph
Simon Peyton-Jones wrote:
Folks
The concrete is setting fast, but Ross points out that the instance for Enum (Ratio a) is inconsistent with that for Enum Float and Enum Double. (It's strange that these non-integral types are in Enum, but we're stuck with that.)
All three use 'numericEnumFrom' etc for the enumFrom method, but Float and Double define succ and pred thus:
instance Enum Float where succ x = x+1 pred x = x-1
But Ratio does not... it uses the default method for succ/pred which gives
instance Integral a => Enum (Ratio a) where succ x = fromInt (1 + (toInt x))
Thus (succ (1.5 :: Float)) is 2.5 but (succ (3%2)) is 2%1
Furthermore, you'd expect that [x..y] would mean the same as [x,succ x .. y] and that is true for Float/Double but not for Ratio.
So I propose to modify the instance decl for Ratio by adding explicit defns for succ/pred just like those in Float/Double.
Any objections?
Simon
No objection, but here are additional related corrections to be made: (1) In section A (Standard Prelude), in the definition of `numericEnumFromThenTo`, change "n' > n" to "n' >= n", to agree with the last bullet in section 6.3.4. That is, the function definition should read: numericEnumFromThenTo n n' m = takeWhile p (numericEnumFromThen n n') where p | n' >= n = (<= m + (n'-n)/2) | otherwise = (>= m + (n'-n)/2) (2) In section 3.10, change "which Prelude type are in Enum" to "which Prelude types are in Enum". -- Dean
participants (4)
-
Ch. A. Herrmann -
Dean Herington -
Ferenc Wagner -
Simon Peyton-Jones