How does this foldl-based function work: "myreverse = foldl (flip (:)) []"? - haskell

I am learning haskell and I tried to write my own reverse function without using recursion.
The solution is this function:
myreverse = foldl (flip (:)) []
I'm trying to understand what happens during the evaluation of, say:
myreverse [1..5]
I can't understand what flip does here. Can somebody write down what happens here with a step-by-step explanation?

flip is rather easy:
if you have a function f :: a -> b -> c then flip f is a function :: b -> a -> c so it gives you back a new function and flips the order of arguments you have to pass.
(:) :: a -> [a] -> [a] is a function of this pattern and so flip (:) will now be a function that first takes the soon-to-be tail and then the new head and returns you a new list with those:
(flip (:)) [2..4] 1
= (:) 1 [2..4]
= 1 : [2..4]
= [1,2,3,4]
now you need this here because foldl is defined this way:
foldl :: (b -> a -> b) -> b -> [a] -> b
you see - the function you have to pass will here be one that first takes a list and then an element and returns a new list
this will now fold all into something like this:
myreverse [1..5]
= foldl (flip (:)) [] [1,2,3,4,5]
= foldl (flip (:)) (((flip (:)) [] 1)) [2,3,4,5]
= foldl (flip (:)) (1:[]) [2,3,4,5]
= foldl (flip (:)) (((flip (:)) [1] 2)) [3,4,5]
= foldl (flip (:)) (2:[1]) [3,4,5]
= ...
= [5,4,3,2,1]

You might try in ghci what flip does:
:t (:)
(:) 1 [2..4]
[1,2,3,4]
:t flip (:)
(flip (:)) [2..4] 1
[1,2,3,4]

Related

Can `foldr` and `foldl` be defined in terms of each other?

Can foldr and foldl be defined in terms of each other?
Programming in Haskell by Hutton says
What do we need to define manually? The minimal complete definition for an instance of the
Foldable class is to define either foldMap or foldr, as all other functions in the class can be derived
from either of these two using the default definitions and the instance for lists.
So how can foldl be defined in terms of foldr?
Can foldr be defined in terms of foldl, so that we can define a Foldable type by defining foldl?
Why is it that in Foldable, fold is defined in terms of foldMap which is defined in terms of foldr, while in list foldable, some specializations of fold are defined in terms of foldl as:
maximum :: Ord a => [a] -> a
maximum = foldl max
minimum :: Ord a => [a] -> a
minimum = foldl min
sum :: Num a => [a] -> a
sum = foldl (+) 0
product :: Num a => [a] -> a
product = foldl (*) 1
? Can they be rewritten as
maximum :: Ord a => [a] -> a
maximum = foldr max
minimum :: Ord a => [a] -> a
minimum = foldr min
sum :: Num a => [a] -> a
sum = foldr (+) 0
product :: Num a => [a] -> a
product = foldr (*) 1
Thanks.
In general, neither foldr nor foldl can be implemented in terms of each other. The core operation of Foldable is foldMap, from which all the other operations may be derived. Neither foldr nor foldl are enough. However, the difference only shines through in the case of infinite or (partially) undefined structures, so there's a tendency to gloss over this fact.
#DamianLattenero has shown the "implementations" of foldl and foldr in terms of one another:
foldl' c = foldr (flip c)
foldr' c = foldl (flip c)
But they do not always have the correct behavior. Consider lists. Then, foldr (:) [] xs = xs for all xs :: [a]. However, foldr' (:) [] /= xs for all xs, because foldr' (:) [] xs = foldl (flip (:)) n xs, and foldl (in the case of lists) has to walk the entire spine of the list before it can produce an output. But, if xs is infinite, foldl can't walk the entire infinite list, so foldr' (:) [] xs loops forever for infinite xs, while foldr (:) [] xs just produces xs. foldl' = foldl as desired, however. Essentially, for [], foldr is "natural" and foldl is "unnatural". Implementing foldl with foldr works because you're just losing "naturalness", but implementing foldr in terms of foldl doesn't work, because you cannot recover that "natural" behavior.
On the flipside, consider
data Tsil a = Lin | Snoc (Tsil a) a
-- backwards version of data [a] = [] | (:) a [a]
In this case, foldl is natural:
foldl c n Lin = n
foldl c n (Snoc xs x) = c (foldl c n xs) x
And foldr is unnatural:
foldr c = foldl (flip c)
Now, foldl has the good, "productive" behavior on infinite/partially undefined Tsils, while foldr does not. Implementing foldr in terms of foldl works (as I just did above), but you cannot implement foldl in terms of foldr, because you cannot recover that productivity.
foldMap avoids this issue. For []:
foldMap f [] = mempty
foldMap f (x : xs) = f x <> foldMap f xs
-- foldMap f = foldr (\x r -> f x <> r) mempty
And for Tsil:
foldMap f Lin = mempty
foldMap f (Snoc xs x) = foldMap f xs <> f x
-- foldMap f = foldl (\r x -> r <> f x) mempty
Now,
instance Semigroup [a] where
[] <> ys = ys
(x : xs) <> ys = x : (xs <> ys)
-- (<>) = (++)
instance Monoid [a] where mempty = []
instance Semigroup (Tsil a) where
ys <> Lin = ys
ys <> (Snoc xs x) = Snoc (ys <> xs) x
instance Monoid (Tsil a) where mempty = Lin
And we have
foldMap (: []) xs = xs -- even for infinite xs
foldMap (Snoc Lin) xs = xs -- even for infinite xs
Implementations for foldl and foldr are actually given in the documentation
foldr f z t = appEndo (foldMap (Endo . f) t ) z
foldl f z t = appEndo (getDual (foldMap (Dual . Endo . flip f) t)) z
f is used to turn each a in the t a into a b -> b (Endo b), and then all the b -> bs are composed together (foldr does it one way, while foldl composes them backwards with Dual (Endo b)) and the final b -> b is then applied to the initial value z :: b.
foldr is replaced with foldl in specializations sum, minimum, etc. in the instance Foldable [], for performance reasons. The idea is that you can't take the sum of an infinite list anyway (this assumption is false, but it's generally true enough), so we don't need foldr to handle it. Using foldl is, in some cases, more performant than foldr, so foldr is changed to foldl. I would expect, for Tsil, that foldr is sometimes more performant than foldl, and therefore sum, minimum, etc. can be reimplemented in terms of foldr, instead of fold in order to get that performance improvement. Note that the documentation says that sum, minimum, etc. should be equivalent to the forms using foldMap/fold, but may be less defined, which is exactly what would happen.
Bit of an appendix, but I think it's worth noticing that:
genFoldr c n [] = n; genFoldr c n (x : xs) = c x (genFoldr c n xs)
instance Foldable [] where
foldl c = genFoldr (flip c)
foldr c = foldl (flip c)
-- similarly for Tsil
is actually a valid, lawful Foldable instance, where both foldr and foldl are unnatural and neither can handle infinite structures (foldMap is defaulted in terms of foldr, and thus won't handle infinite lists either). In this case, foldr and foldl can be written in terms of each other (foldl c = foldr (flip c), though it is implemented with genFoldr). However, this instance is undesirable, because we would really like a foldr that can handle infinite lists, so we instead implement
instance Foldable [] where
foldr = genFoldr
foldl c = foldr (flip c)
where the equality foldr c = foldl (flip c) no longer holds.
Here's a type for which neither foldl nor foldr can be implemented in terms of the other:
import Data.Functor.Reverse
import Data.Monoid
data DL a = DL [a] (Reverse [] a)
deriving Foldable
The Foldable implementation looks like
instance Foldable DL where
foldMap f (DL front rear) = foldMap f front <> foldMap f rear
Inlining the Foldable instance for Reverse [], and adding the corresponding foldr and foldl,
foldMap f (DL front rear) = foldMap f front <> getDual (foldMap (Dual . f) (getReverse rear))
foldr c n (DL xs (Reverse ys)) =
foldr c (foldl (flip c) n ys) xs
foldl f b (DL xs (Reverse ys)) =
foldr (flip f) (foldl f b xs) ys
If the front list is infinite, then foldr defined using foldl won't work. If the rear list is infinite, then foldl defined using foldr won't work.
In the case of lists: foldl can be defined in terms of foldr but not vice-versa.
foldl f a l = foldr (\b e c -> e (f c b)) id l a
For other types which implement Foldable: the opposite may be true.
Edit 2:
There is another way also that satisfy (based on this article) for foldl:
foldl f a list = (foldr construct (\acc -> acc) list) a
where
construct x r = \acc -> r (f acc x)
Edit 1
Flipping the arguments of the function will not create a same foldr/foldl, meaning this examples does not satisfy the equality of foldr-foldl:
foldr :: Foldable t => (a -> b -> b) -> b -> t a -> b
and foldl in terms of foldr:
foldl' :: Foldable t => (b -> a -> b) -> b -> t a -> b
foldl' f b = foldr (flip f) b
and foldr:
foldr' :: Foldable t => (a -> b -> b) -> b -> t a -> b
foldr' f b = foldl (flip f) b
The converse is not true, since foldr may work on infinite lists, which foldl variants never can do. However, for finite lists, foldr can also be written in terms of foldl although losing laziness in the process. (for more check here)
Ando also not satisfy this examples:
foldr (-) 2 [8,10] = 8 - (10 - 2) == 0
foldl (flip (-)) 2 [8,10] = (flip (-) (flip (-) 2 8) 10) == 4

ConcatMap in haskell without ++

I'm trying to write the code for Haskell concatmap without using the ++ operator where
concatMap :: (a -> [b]) -> [a] -> [b]
and producing the same result of
concatMap f = foldr ((++) . f) []
I'm quite new to Haskell and this was just an exercise I found. Actually, I do not even know if this can be done.
Here's a way that makes the state of the computation explicit:
concatMap :: (a -> [b]) -> [a] -> [b]
concatMap f = go []
where
-- We have b values; use one.
go (b:bs) as = b : go bs as
-- No bs left; get some more.
go [] (a:as) = go (f a) as
-- Nothing left; we're done.
go [] [] = []
This maintains the current list of bs, filling it up whenever it's empty.
This might be cheating, but how about:
myConcatMap f s = concat (map f s)
The concat function uses some sort of ++ in its source code, so that is why you might not like it. You can try to use an alternative concat that does list comprehensions, it has a more "from scratch" feeling.
myconcat ll = [y | x <- ll, y <- x]
You can use the fact that foldr (:) = flip (++)
concatMap f = foldr (flip (foldr (:)) . f) []
Or pointfree:
concatMap = flip foldr [] . (flip (foldr (:)) .)

Haskell pointfree programming

I am trying to understand pointfree programming in Haskell and I questions on some examples, because I don't really understand the explanation given when the errors occur.
1) I have a cycle function defined below:
myCycle :: [a] -> [a]
myCycle = foldr (++) [] . repeat
Why does myCycle = foldr (++) [] $ repeat not work?
2) Add every element of a list with 2 then add with another list
sum :: [Int] -> [Int] -> [Int]
sum s = zipWith (+) . map (+ 2) $ s
Why does the function has the same result with sum s = zipWith (+) $ map (+ 2) s and why does sum l1 l2 = zipWith (+) . map (+ 2) $ l1 $ l2 not work
First of all, let's list all types:
foldr :: (a -> b -> b) -> b -> [a] -> b
(++) :: [a] -> [a] -> [a]
[] :: [a]
repeat :: a -> [a]
(.) :: (b -> c) -> (a -> b) -> a -> c
($) :: (a -> b) -> a -> b
foldr (++) :: [a] -> [[a]] -> [a]
foldr (++) [] :: [[a]] -> [a]
Now, as you can see, ($) doesn't change the type at all. It's just so that its fixity makes sure that you can use it instead of parentheses. Let's see how they differ:
($) (foldr (++) []) :: [[a]] -> [a]
(.) (foldr (++) []) :: (b -> [[a]]) -> b -> [a]
Since repeat has type c -> [c], it doesn't work with ($). It sure does with (.), since c ~ [a] works fine.
So always keep in mind that ($) doesn't do anything on its own. It merely changes the precedence/fixity. Also, it sometimes helps if you use prefix notation instead of infix if you try to understand/come to pointfree code:
sum l1 l2 = zipWith (+) (map (+2) l1) l2
= zipWith (+) (map (+2) l1) $ l2
= ($) (zipWith (+) (map (+2) l1)) l2
-- get rid of both ($) and l2:
sum l1 = zipWith (+) (map (+2) l1)
= (zipWith (+)) ((map (+2)) l1)
= f (g l1) -- f = zipWith (+), g = map (+2)
= (f . g) l1
= (zipWith (+) . (map (+2)) l1 -- substitute f and g again
= zipWith (+) . (map (+2) $ l1
-- get rid of $ and l1:
sum = zipWith (+) . map (+2)
If you check the signatures in GHCi you get
Prelude> :t (.)
(.) :: (b -> c) -> (a -> b) -> a -> c
Prelude> :t ($)
($) :: (a -> b) -> a -> b
This shows that the dot operator operates on functions while the dollar operator is just a strange version of the normal function application (it allows you to write e.g. f (g (h x)) as f $ g $ h $ x).
In your mycycle example foldr (++) [] has signature [[a]] -> [a] and repeat has a -> [a]. So when typing foldr (++) [] $ repeat Haskell tries to match the function signature a -> [a] with the first argument of the foldr expression which is [[a]], a list of lists. This fails and gives an error. The dot operator actually expects a function and everything is fine.
In your second example, sum s = zipWith (+) . map (+ 2) $ s is equivalent to sum = zipWith (+) . map (+ 2). Type inference regards zipWith (+) as a unary function returning a unary function and is able to match it to the argument expected by the dot operator. So here the functions are first composed and then applied to s. In sum s = zipWith (+) $ map (+ 2) s there is no composition, just application: first map (+ 2) is applied to s and then zipWith (+) is applied to the result.
The point of pointfree programming is to use less function application and more function composition.
myCycle = foldr (++) [] $ repeat is equivalent to myCycle z = (foldr (++) [] $ repeat) z.
(x $ y) z is equal to (x y) z; (x . y) z is equal to x (y z).
The best way to gain insight on these things in haskell is to just manually expand things out, based on their definitions.
(f . g) = \x -> f (g x)
f $ x = f x
So, whenever we see (f . g), we can replace it with \x -> f (g x). and when we see f $ x, we can replace it with f x. let's see where this takes us!
myCycle = foldr (++) [] . repeat
Hm, let's expand out the definition of .:
myCycle = \x -> foldr (++) [] (repeat x)
myCycle x = foldr (++) [] (repeat x)
Sweet, this basically does exactly what we'd want it to do. Concatenate a list of repeating x's.
Now, let's see if you had done $:
myCycle = foldr (++) [] $ repeat
That becomes:
myCycle = foldr (++) [] repeat
That's nice and all, but this doesn't make any sense. the third argument of foldr should be a list, but you gave it a function (repeat). repeat is definitely not a list, so this whole affair is kind of silly.
We can try the same thing here:
sum s = zipWith (+) . map (+ 2) $ s
sum s = (zipWith (+) . map (+ 2)) s
sum s = zipWith (+) (map (+ 2) s) -- (f . g) x = f (g x)
And look at the other formulation:
sum s = zipWith (+) $ map (+ 2) s
sum s = (zipWith (+)) (map (+ 2) s)
sum s = zipWith (+) (map (+ 2) s) -- redundant parentheses
and...they're the same thing!
Let's try seeing what the last one does:
sum l1 l2 = zipWith (+) . map (+ 2) $ l1 $ l2
sum l1 l2 = zipWith (+) . map (+ 2) $ (l1 l2)
Oops...you're trying to do l1 l2, or apply l1 as if it were a function. That doesn't make any sense. l1 is a list, not a function. So, already here you can see why this is nonsense :)

Verifying foldl implementation in terms of foldr

I want to verify following implementation of foldl in terms foldr is correct:
foldl4 = foldr . flip
I used following tests in HUGS:
foldl4 (+) 3 []
foldl4 (+) 3 [1,2,3]
They worked.
Please suggest any more tests I could do.
Thanks
here is a simple test: foldl (flip (:)) [] should be reverse...
if you want to test foldr vs foldl you probably should not use commutative operations ;)
here is some proof straight from GHCi:
λ> foldl (flip (:)) [] [1..5]
[5,4,3,2,1]
λ> foldl4 (flip (:)) [] [1..5]
[1,2,3,4,5]
and as flip (+) = (+) you can guess straight from your definition:
foldl4 (+) y xs
{ def }
= foldr (flip (+)) y xs
{ flip (+) = (+) }
= foldr (+) y xs
if you want some hint of how to do foldl with foldr: you should use functions for the accumulator/state/b part of foldr :: (a -> b -> b) -> b -> [a] -> b - think of continuation passing and try to replace the : in
x : (y : (z : [])
with some smart function to get
((b `f` x) `f` y) `f` z
remember you want to mimick
foldl f b [x,y,z] = ((b `f` x) `f` y) `f` z
with foldr which basically replaces : with it's first parameter and [] with it's second if you pass [x,y,z] as the 3rd:
foldr f' b' [x,y,z] = x `f'` (y `f'` (z `f'` b'))
and you now want to shift the parens
Those two are not the same. foldl and foldr do semantically different things, but flip only induces a syntactic difference, so foldr . flip cannot ever ever be foldl.
Something that is foldl for example (on finite lists) is
foldl5 = (.) (. reverse) . foldr . flip
That first part might look confusing, but it basically applies reverse to the third argument rather than the first.

foldl . foldr function composition - Haskell

So, I'm really frying my brain trying do understand the foldl.foldr composition.
Here is a example:
(foldl.foldr) (+) 1 [[1,2,3],[4,5,6]]
The result is 22, but what's really happening here?
To me it looks like this is what is happening: foldl (+) 1 [6,15].
My doubt is related to the foldr part. Shouldn't it add the 1 to all the sub-lists? Like this: foldr (+) 1 [1,2,3].
In my head the 1 is added just one time, is it right? (probably not, but I want to know how/why!).
I'm very confused (and perhaps making all the confusion, haha).
Thank you!
(foldl.foldr) (+) 1 [[1,2,3],[4,5,6]]
becomes
foldl (foldr (+)) 1 [[1,2,3],[4,5,6]]
So you get
foldl (foldr (+)) (foldr (+) 1 [1,2,3]) [[4,5,6]]
after the first step of foldl, or
foldl (foldr (+)) 7 [[4,5,6]]
if we evaluate the applied foldr (unless the strictness analyser kicks in, it would in reality remain an unevaluated thunk until the foldl has traversed the entire list, but the next expression is more readable with it evaluated), and that becomes
foldl (foldr (+)) (foldr (+) 7 [4,5,6]) []
and finally
foldl (foldr (+)) 22 []
~> 22
Let's examine foldl . foldr. Their types are
foldl :: (a -> b -> a) -> (a -> [b] -> a)
foldr :: (c -> d -> d) -> (d -> [c] -> d)
I intentionally used distinct type variables and I added parentheses so that it becomes more apparent that we view them now as functions of one argument (and their results are functions). Looking at foldl we see that it is a kind of lifting function: Given a function that produces a from a using b, we lift it so that it works on [b] (by repeating the computation). Function foldr is similar, just with arguments reversed.
Now what happens if we apply foldl . foldr? First, let's derive the type: We have to unify the type variables so that the result of foldr matches the argument of foldl. So we have to substitute: a = d, b = [c]:
foldl :: (d -> [c] -> d) -> (d -> [[c]] -> d)
foldr :: (c -> d -> d) -> (d -> [c] -> d)
So we get
foldl . foldr :: (c -> d -> d) -> (d -> [[c]] -> d)
And what is its meaning? First, foldr lifts the argument of type c -> d -> d to work on lists, and reverses its arguments so that we get d -> [c] -> d. Next, foldl lifts this function again to work on [[c]] - lists of [c].
In your case, the operation being lifted (+) is associative, so we don't have care about the order of its application. The double lifting simply creates a function that applies the operation on all the nested elements.
If we use just foldl, the effect is even nicer: We can lift multiple times, like in
foldl . foldl . foldl . foldl
:: (a -> b -> a) -> (a -> [[[[b]]]] -> a)
Actually, (foldl.foldr) f z xs === foldr f z (concat $ reverse xs).
Even if f is an associative operation, the correct sequence of applications matters, as it can have an impact on performance.
We begin with
(foldl.foldr) f z xs
foldl (foldr f) z xs
writing with g = foldr f and [x1,x2,...,xn_1,xn] = xs for a moment, this is
(...((z `g` x1) `g` x2) ... `g` xn)
(`g` xn) ((`g` xn_1) ... ((`g` x1) z) ... )
foldr f z $ concat [xn,xn_1, ..., x1]
foldr f z $ concat $ reverse xs
So in your case the correct reduction sequence is
(foldl.foldr) 1 [[1,2,3],[4,5,6]]
4+(5+(6+( 1+(2+(3+ 1)))))
22
To wit,
Prelude> (foldl.foldr) (:) [] [[1..3],[4..6],[7..8]]
[7,8,4,5,6,1,2,3]
Similarly, (foldl.foldl) f z xs == foldl f z $ concat xs. With snoc a b = a++[b],
Prelude> (foldl.foldl) snoc [] [[1..3],[4..6],[7..8]]
[1,2,3,4,5,6,7,8]
Also, (foldl.foldl.foldl) f z xs == (foldl.foldl) (foldl f) z xs == foldl (foldl f) z $ concat xs == (foldl.foldl) f z $ concat xs == foldl f z $ concat (concat xs), etc.:
Prelude> (foldl.foldl.foldl) snoc [] [[[1..3],[4..6]],[[7..8]]]
[1,2,3,4,5,6,7,8]
Prelude> (foldl.foldr.foldl) snoc [] [[[1..3],[4..6]],[[7..8]]]
[7,8,1,2,3,4,5,6]
Prelude> (foldl.foldl.foldr) (:) [] [[[1..3],[4..6]],[[7..8]]]
[7,8,4,5,6,1,2,3]

Resources