3

Suppose we have a list

x = [1..10]

and we intend to create another list y using this in this manner :

y= [a|a<-x]

So while creating the list y from x, it accesses each element of x( from 1 to 10) and inserts it in y in the same order. As a list in haskell is singly linked list, we can insert a new element only at its head. So first it inserts 1 to [] & we have [1]. Then it inserts 2 to its head & so we have [2,1]. Then it inserts 3 & we have [3,2,1] & so on. So ultimately we should get y as [10,9..1]. But instead we get y as [1..10]. Why is that so?

Will Ness
  • 70,110
  • 9
  • 98
  • 181
Arpan Biswas
  • 185
  • 2
  • 12
  • 1
    This is a good explanation on how to desugar the list comprehension syntax: http://stackoverflow.com/a/8029698/1013393 – sjakobi Aug 18 '16 at 11:07

3 Answers3

6

Because it "inserts" them at list's tail (while the list is being built), not head (cf. "tail recursion modulo cons"):

[a | a <- [1..10]]     ==
[a | a <- (1:[2..10])] ==      -- [a | a <- ([1] ++ [2..10])]
1 : [a | a <- [2..10]]         -- [1] ++ [a | a <- [2..10]]

This is because

[a | a <- (xs ++ ys)]  ==  [a | a <- xs] ++ [a | a <- ys]

and

[a | a <- ys]  ==  ys
Will Ness
  • 70,110
  • 9
  • 98
  • 181
6

The way you are thinking about list comprehensions is not quite correct. When you see a comprehension

y <- [f a | a <- x]

you shouldn't think about the successive result being added to the front of an (initially empty) result list.

Instead, think of each f a being added to the front of the result of running the list comprehension on the rest of x. That is,

[f a | a <- x:xs] == f x : [f a | a <- xs]

This is a recursive approach - by assuming that the result list exists for the tail of the list, you can add the next result to the front of it.

Chris Taylor
  • 46,912
  • 15
  • 110
  • 154
4

It's instructive to look at exactly how list comprehensions desugar to monadic computations, rather than use the intuition for how they work.

[a | a <- [1..3]] == do {a <- [1..3]; return a}   -- desugared comprehension
                  == [1..3] >>= return a          -- desugared do
                  == concat (map return [1..3])   -- definition of >>=
                  == concat [[1], [2], [3]]       -- defintiion of return
                  == [1,2,3]                      -- definition of concat
chepner
  • 497,756
  • 71
  • 530
  • 681