Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does map return an additional element when using ranges in Haskell?

I've just started learning Haskell and found a strange thing.

Let we have a list:

ghci> [0,2..5]
[0,2,4]

It has 3 elements. When I use map with this list I get 3 element as output, for example:

ghci> map (+ 1) [0,2..5]
[1,3,5]
ghci> map (* 2) [0,2..5]
[0,4,8]
ghci> map (`div` 2) [0,2..5]
[0,1,2]

But when I use fractional division I get 4 elements in output list:

ghci> map (/ 2) [0,2..5]
[0.0,1.0,2.0,3.0]
ghci> length (map (/ 2) [0,2..5])
4

Could you please explain why map may return more elements then it was?

Thank you!

like image 512
Vadik Avatar asked Sep 02 '15 23:09

Vadik


People also ask

How does map function work in Haskell?

The map() function takes two parameters namely a list and the function to be applied on each element in the list and returns a new list as the output. The map() function is available in Data. Map module in Haskell programming language.

Can Haskell stack overflow?

There is no call stack in Haskell. Instead we find a pattern matching stack whose entries are essentially case expressions waiting for their scrutinee to be evaluated enough that they can match a constructor (WHNF).


1 Answers

It's due to the implementation of Enum for Float and Double:

> [0,2..5] :: [Float]
[0.0,2.0,4.0,6.0]

It's not map doing it, but Float. Specifically, if you call enumFromThenTo 0 2 5 :: [Float], you'll get the same list. You'll see the same results for Double.

This is hinted at in the haskell report, but the behavior is definitely non-obvious. Essentially, it comes down to the implementation of numericEnumFromThenTo (we're getting into some Haskell internals here), which is used by the Enum Float instance:

numericEnumFromThenTo n n' m = takeWhile p (numericEnumFromThen n n')  
    where  
        p | n' >= n   = (<= m + (n' - n) / 2)  
          | otherwise = (>= m + (n' - n) / 2) 

numericEnumFromThen n m = iterate (+ (m - n)) n

So you have numericEnumFromThen 0.0 2.0 generating the list [0.0,2.0,4.0,6.0,8.0,...], then you do takeWhile p on that, which in this case is equivalent to the function \x -> x <= 5.0 + (2.0 - 0.0) / 2, or more simply \x -> x <= 6.0, which is why 6.0 is included in the output list of [0.0,2.0..5.0].

I can't explain why it's implemented this way, that's pretty baffling to me too, but hopefully I've answered the how for its implementation.

like image 182
bheklilr Avatar answered Nov 16 '22 00:11

bheklilr