Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reverse range implementation

Tags:

haskell

For the record, I'm a Haskell noob. I'm trying to understand why a range can be declared as

[1..10] -- yields [1,2,3,4,5,6,7,8,9,10]

and not

[10..1] -- yields []

It seems simple enough to implement it like this:

(.:.) :: Enum a => a -> a -> [a]
(.:.) a b =
   | a == b = []
   | a > b = a : (a - 1) .:. b
   | a < b = a : (a + 1) .:. b

What am I not understanding here?

like image 706
dopatraman Avatar asked Jul 31 '15 19:07

dopatraman


2 Answers

Try [10,9..1] instead of [10..1]

[10..1] desugars to enumFromTo

[10,9..1] desugars to enumFromThenTo

Observe how the functions behave in GHCI:

Prelude> [10..1]
[]
Prelude> [10,9..1]
[10,9,8,7,6,5,4,3,2,1]
Prelude> :t enumFromTo
enumFromTo :: Enum a => a -> a -> [a]
Prelude> :t enumFromThenTo
enumFromThenTo :: Enum a => a -> a -> a -> [a]
Prelude> enumFromTo 10 1
[]
Prelude> :t enumFromThenTo 10 9 1
[10,9,8,7,6,5,4,3,2,1]

The new behavior that you implemented in (.:.) will rarely be a desirable default, as lists generated this way can no longer always be assumed to be in ascending order. Hence the necessity to make your descending intention explicit with [x,(pred x)..y] or enumFromThenTo.

like image 199
recursion.ninja Avatar answered Oct 21 '22 13:10

recursion.ninja


Almost always when you have an expression like [1..n], and n happens to be 0, you want the value to be [] rather than [1,0]. For example consider

factorial n = product [1..n]

Then we want

factorial 0  ~~>  product [1..0]  ~~>  product []  ~~>  1   -- correct

not

factorial 0  ~~>  product [1..0]  ~~>  product [1,0]  ~~>  0   -- oops

That's why [m..n] does not produce a descending list when m > n.

(If m >= n+2 (for example if in [1..n], n is negative) then it's quite likely an error condition and arguably [m..n] ought to be an error; but it's still defined by the language standard to be [].)

like image 34
Reid Barton Avatar answered Oct 21 '22 14:10

Reid Barton