Decrementing ranges in Haskell
Basically, because [10..1]
is translated to enumFromTo 10 1
which itself has the semantics to create a list by taking all elements less-than 1
which result from counting upward (with step-size +1
) from (including) 10
.
Whereas [10, 9..1]
is translated to enumFromToThen 10 9 1
which explicitly states the counting step-size as 9-10
, i.e. -1
(which is hard-coded to +1
for enumFromTo
)
A more accurate specification can be found in the Haskell Report (6.3.4 The Enum Class):
enumFrom :: a -> [a] -- [n..]
enumFromThen :: a -> a -> [a] -- [n,n'..]
enumFromTo :: a -> a -> [a] -- [n..m]
enumFromThenTo :: a -> a -> a -> [a] -- [n,n'..m]
For the types
Int
andInteger
, the enumeration functions have the following meaning:
The sequence
enumFrom e1
is the list[e1,e1+1,e1+2,...]
.The sequence
enumFromThen e1 e2
is the list[e1,e1+i,e1+2i,...]
, where the increment, i, is e2-e1. The increment may be zero or negative. If the increment is zero, all the list elements are the same.The sequence
enumFromTo e1 e3
is the list[e1,e1+1,e1+2,...e3]
. The list is empty ife1 > e3
.The sequence
enumFromThenTo e1 e2 e3
is the list[e1,e1+i,e1+2i,...e3]
, where the increment,i
, ise2-e1
. If the increment is positive or zero, the list terminates when the next element would be greater thane3
; the list is empty ife1 > e3
. If the increment is negative, the list terminates when the next element would be less thane3
; the list is empty ife1 < e3
.
Arithmetic sequence notation is just syntactic sugar for functions from the Enum
class.
[a..] = enumFrom a
[a..b] = enumFromTo a b
[a, b..] = enumFromThen a b
[a, b..c] = enumFromThenTo a b c
As for why they weren't defined to automatically reverse, I can only speculate but here are some possible reasons:
If
a
andb
are defined elsewhere, it would be harder to tell at a glance in which direction[a..b]
would go.It has nicer mathematical properties to reason about. You don't have to add special cases for when the sequence would be reversed.
If you want to generate a list from a
to b
regardless of whether a < b
, you can use the following:
[a, a + (signum $ b - a)..b]