Actually, because Haskell's laziness is call by need, having defined
xs = 1 : map (+1) xs
then calling e.g. print xs
proceeds as
xs
xs where xs = 1 : map (+ 1) xs
xs where xs = x1 : xs2 ; x1 = 1 ; xs2 = map (+ 1) xs
x1 : xs2 where xs2 = map (+ 1) (x1 : xs2) ; x1 = 1
x1 : xs2 where xs2 = (x1+1) : map (+ 1) xs2 ; x1 = 1
x1 : xs2 where xs2 = x2 : xs3 ; x2 = x1+1 ; xs3 = map (+ 1) xs2 ; x1 = 1
1 : x2 : xs3 where xs3 = map (+ 1) (x2 : xs3) ; x2 = 2
1 : x2 : xs3 where xs3 = (x2+1) : map (+ 1) xs3 ; x2 = 2
1 : 2 : x3 : xs4 where xs4 = map (+ 1) (x3 : xs4) ; x3 = 3
1 : 2 : 3 : x4 : xs5 where xs5 = map (+ 1) (x4 : xs5) ; x4 = 4
.....
so that there's no repeated computations of n+1
by repeated additions of 1
s. The n
is already computed.
We just give a name to every place, so that the name holds a computation to be performed, at first, and then its value, afterwards. Since the name is shared among other computations, there's no repeated calculations of its value.
Same thing happens with the calculation of the Fibonacci numbers list.
So then xs
indeed generates the list [1,2..]
. The process of repeated additions of 1
is naturally expressed as iterate (+1) 1
, as already noted in another answer.
We could also define this as
nums1 = xs where
ys = 1 : ys
xs = zipWith (+) (0 : xs) ys
which is perhaps what you were trying to express.
The definition of a list by zipping with itself shifted by one step (as in the Fibonacci list definition) gives the mapping function access to two previous elements of the list simultaneously while defining the current element.
Similarly, the definition of a list by zipping itself shifted by one step and another list (as with the definition of nums1
above) gives the mapping function access to the previous element while defining a current one.
We say "mapping function" because zipWith
is a binary map
:
zipWith f xs ys = map (\(x,y) -> f x y) (zip xs ys)