In short: This is intended behavior.
The [x, y..z]
expression is syntactical sugar for enumFromThenTo x y z
with enumFromThenTo :: a -> a -> a -> [a]
.
For Integer
s it is implemented like:
instance Enum Integer where
# ...
enumFromThenTo x y lim = enumDeltaToInteger x (y-x) lim
So it will call enumDeltaToInteger 2 0 (-20)
. The enumDeltaToInteger
is implemented with [src]:
enumDeltaToInteger :: Integer -> Integer -> Integer -> [Integer]
enumDeltaToInteger x delta lim
| delta >= 0 = up_list x delta lim
| otherwise = dn_list x delta lim
Soo it is considered to be an up_list
, and the up_list
will increase until it has reached a value larger than lim
:
up_list :: Integer -> Integer -> Integer -> [Integer]
up_list x0 delta lim = go (x0 :: Integer)
where
go x | x > lim = []
| otherwise = x : go (x+delta)
This is how it has been described in the Haskell'10 report on the Enum
class:
The sequence enumFromThenTo e1 e2 e3
is the list [e1,e1 + i,e1 + 2i,…e3]
, where the increment, i
, is e2 − e1
. If the increment is positive or zero, the list terminates when the next element would be greater than e3
; the list is empty if e1 > e3
. If the increment is negative, the list terminates when the next element would be less than e3
; the list is empty if e1 < e3
.
So the document says that if the "step" is zero or more, and e1 > e3
, then the result is the empty list.
It is indeed a "tricky" case however. I personally agree that using a special case for 0
as "step" would make sense (although I'm not per se saying this is more favorable than using the up_list
implementation). It is however how things are defined.