foldr
defines a family of equations,
foldr g n [] = n
foldr g n [x] = g x (foldr g n []) = g x n
foldr g n [x,y] = g x (foldr g n [y]) = g x (g y n)
foldr g n [x,y,z] = g x (foldr g n [y,z]) = g x (g y (g z n))
----- r ---------
and so on. g
is a reducer function,
g x r = ....
accepting as x
an element of the input list, and as r
the result of recursively processing the rest of the input list (as can be seen in the equations).
map
, on the other hand, defines a family of equations
map f [] = []
map f [x] = [f x] = (:) (f x) [] = ((:) . f) x []
map f [x,y] = [f x, f y] = ((:) . f) x (((:) . f) y [])
map f [x,y,z] = [f x, f y, f z] = ((:) . f) x (((:) . f) y (((:) . f) z []))
= (:) (f x) ( (:) (f y) ( (:) (f z) []))
The two families simply exactly match with
g = ((:) . f) = (\x -> (:) (f x)) = (\x r -> f x : r)
and n = []
, and thus
foldr ((:) . f) [] xs == map f xs
We can prove this rigorously by mathematical induction on the input list's length, following the defining laws of foldr
,
foldr g n [] = []
foldr g n (x:xs) = g x (foldr g n xs)
which are the basis for the equations at the top of this post.
Modern Haskell has Fodable
type class with its basic fold
following the laws of
fold(<>,n) [] = n
fold(<>,n) (xs ++ ys) = fold(<>,n) xs <> fold(<>,n) ys
and the map
is naturally defined in its terms as
map f xs = foldMap (\x -> [f x]) xs
turning [x, y, z, ...]
into [f x] ++ [f y] ++ [f z] ++ ...
, since for lists (<>) == (++)
. This follows from the equivalence
f x : ys == [f x] ++ ys
This also lets us define filter
along the same lines easily, as
filter p xs = foldMap (\x -> [x | p x]) xs
To your specific question, the expansion is correct, except that (*2 x)
should be written as ((*2) x)
, which is the same as (x * 2)
. (* 2 x)
is not a valid Haskell (though valid Lisp :) ).
Functions like (*2)
are known as "operator sections" -- the missing argument goes into the empty slot: (* 2) 3 = (3 * 2) = (3 *) 2 = (*) 3 2
.
You also asked for some links: see e.g. this, this and this.