I can't understand the following behavior of ranges in Haskell. Enumerating 1 to 1 gives me a list contain only 1, and 2 to 2 gives me a list contain only 2 as given below.
Prelude> [1..1]
[1]
Prelude> [2..2]
[2]
But enumerating infinity to infinity gives me a list which is infinite in length and all the elements are infinity, as shown below.
Prelude> [1/0..1/0]
[Infinity,Infinity,Infinity,Infinity,Infinity,Infinity,Interrupted.
I am aware that Infinity is a concept not to be treated as a number, but what justifies that behavior ?
Haskell's Double
(the default you get when using /
) follows the IEEE 754 standard for floating point numbers, which defines how Infinity
behaves. This is why 1/0
is Infinity
.
By this standard (and, to be fair, by logic), Infinity + 1 == Infinity
, and the Enum
instance for Double
just adds 1
each time.
This is another sign that the Enum
instance for Double
is not entirely well-formed and does not conform to the expectations we usually have for Enum
instances. So, as a rule of thumb, you should probably avoid using ..
for floating point numbers: even if you understand how it works, it will be confusing for others reading your code. As another example of confusing behavior, consider:
Prelude> [0.1..1]
[0.1,1.1]
If you want to get into a lot more details about the Enum
instance for Double
, you can read through this pretty lengthy Haskell-cafe thread.
As Luis Wasserman and Tikhon Jelvis have indicated, the basic problem is that the Num
and Enum
instances for Float
and Double
are weird, and really should not exist at all. In fact, the Enum
class itself is pretty weird, as it attempts to serve several different purposes at once, none of them well—it is probably best considered a historical accident and a convenience rather than a good example of what a typeclass should look like. The same is true, to a significant extent, of the Num
and Integral
classes. When using any of these classes, you must pay close attention to the specific types you are operating on.
The enumFromTo
methods for floating point are based on the following function:
numericEnumFromTo :: (Ord a, Fractional a) => a -> a -> [a]
numericEnumFromTo n m = takeWhile (<= m + 1/2) (numericEnumFrom n)
So 2 <= 1+1/2
is false, but because of the weirdness of floating point, infinity <= infinity + 1/2
is true.
As Tikhon Jelvis indicates, it's generally best not to use any Enum
methods, including numeric ranges, with floating point.
I believe the implementation of [a..b]
continues incrementing a
by one until it is greater than b
. This is never going to happen with infinity, so it goes forever.
I believe your code will probably default to Double
the way you've written it, which has well-defined semantics for infinity. IIRC, Haskell follows http://en.wikipedia.org/wiki/IEEE_floating_point.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With