[Haskell-cafe] instance Enum Double considered not entirely great?

Evan Laforge qdunkan at gmail.com
Tue Sep 20 07:09:09 CEST 2011

So I've been excising occurrences of the [n..m] syntax from my code.
The reason is that I had been using [0..5] as a shorthand for [0, 1,
2, 3, 4, 5], mostly in tests.  This works for integral types and it
appears to work for floating point types.

Then I tried switching to a fixed point format, and discovered my
mistake.  Enum is supposed to enumerate every value between the two
points, and the result is memory exhaustion.  I had already been
switching from enumFrom because they accumulate floating point errors,
in favor of a couple of more appropriate functions:

-- | Enumerate an inclusive range.  Uses multiplication instead of successive
-- addition to avoid loss of precision.
-- Also it doesn't require an Enum instance.
range :: (Num a, Ord a) => a -> a -> a -> [a]
range start end step = go 0
    go i
        | val > end = []
        | otherwise = val : go (i+1)
        where val = start + (i*step)

-- | Like 'range', but includes the end.
range_end :: (Num a, Ord a) => a -> a -> a -> [a]
range_end start end step = go 0
    go i
        | val >= end = [end]
        | otherwise = val : go (i+1)
        where val = start + (i*step)

-- | Infinite range.
range_ :: (Num a) => a -> a -> [a]
range_ start step = go 0
    where go i = start + (i*step) : go (i+1)

So I think the Enum instances for Float and Double are sort of a trap.
 They misled me into thinking the .. syntax is the range function
defaulting to a step of 1, led to precision errors I had to debug then
fix (and I've noticed lead to the odd stack overflow question), then
led to more disaster when I switched to an Enum instance that lived up
to Enum's real purpose.  The problem is that the [..] syntax is
convenient and tempting, especially for tests, wanting a numeric range
(rather than enumeration) is very common, and as far as I know the
'range' function isn't in the standard library.

So it seems like one of these things that seem convenient in the short
term but come around and bite you later.  Using the right functions
from the start avoids all that.  Is there any support for the idea of
removing Enum instances for floating point numbers?

More information about the Haskell-Cafe mailing list