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 :)
Related
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.
I was given an assignment in my functional programming course that asks me to rewrite several functions, like map and filter to be tail recursive.
I'm not 100% sure how to go about this yet but I know that you can define functions by calling foldr and foldl. I know foldl is tail recursive, so if I can define say, filter with foldl, would it become tail recursive, too?
There are two ways to make a recursive function tail recursive:
Convert the function to accumulator passing style. This only works in some cases.
Convert the function to continuation passing style. This works in all cases.
Consider the definition of the map function:
map :: (a -> b) -> [a] -> [b]
map _ [] = []
map f (x:xs) = f x : map f xs
In accumulator passing style, we have an additional argument which accumulates the result:
mapA :: (a -> b) -> [a] -> [b] -> [b]
mapA _ [] = id
mapA f (x:xs) = mapA f xs . (f x :)
The original map function can be recovered as follows:
map :: (a -> b) -> [a] -> [b]
map f xs = reverse $ mapA f xs []
Note that we need to reverse the result. This is because mapA accumulates the result in reverse:
> mapA (+1) [1,2,3,4,5] []
> mapA (+1) [2,3,4,5] [2]
> mapA (+1) [3,4,5] [3,2]
> mapA (+1) [3,5] [4,3,2]
> mapA (+1) [5] [5,4,3,2]
> mapA (+1) [] [6,5,4,3,2]
> [6,5,4,3,2]
Now, consider continuation passing style:
mapK :: (a -> b) -> [a] -> ([b] -> r) -> r
mapK _ [] k = k []
mapK f (x:xs) k = mapK f xs (k . (f x :))
The original map function can be recovered as follows:
map :: (a -> b) -> [a] -> [b]
map f xs = mapK f xs id
Note that we do not need to reverse the result. This is because although mapK accumulates the continuations in reverse, yet when finally applied to the base case the continuations are unfolded to produce the result in the correct order:
> mapK (+1) [1,2,3,4,5] id
> mapK (+1) [2,3,4,5] (id . (2:))
> mapK (+1) [3,4,5] (id . (2:) . (3:))
> mapK (+1) [4,5] (id . (2:) . (3:) . (4:))
> mapK (+1) [5] (id . (2:) . (3:) . (4:) . (5:))
> mapK (+1) [] (id . (2:) . (3:) . (4:) . (5:) . (6:))
> (id . (2:) . (3:) . (4:) . (5:) . (6:)) []
> (id . (2:) . (3:) . (4:) . (5:)) [6]
> (id . (2:) . (3:) . (4:)) [5,6]
> (id . (2:) . (3:)) [4,5,6]
> (id . (2:)) [3,4,5,6]
> id [2,3,4,5,6]
> [2,3,4,5,6]
Note, that in both cases we're doing twice the required amount of work:
First, we accumulate an intermediate result in reverse order.
Next, we produce the final result in the correct order.
Some functions can be written efficiently in the accumulator passing style (e.g. the sum function):
sumA :: Num a => [a] -> a -> a
sumA [] = id
sumA (x:xs) = sumA xs . (+ x)
The original sum function can be recovered as follows:
sum :: Num a => [a] -> a
sum xs = sumA xs 0
Note that we don't need to do any post processing on the result.
However, list functions written in tail recursive style always need to be reversed. Hence, we do not write list functions in tail recursive style. Instead, we depend upon laziness to process only as much of the list as required.
It should be noted that continuation passing style is just a special case of accumulator passing style. Since foldl is both tail recursive and uses an accumulator, you can write mapA and mapK using foldl as follows:
mapA :: (a -> b) -> [a] -> [b] -> [b]
mapA f xs acc = foldl (\xs x -> f x : xs) acc xs
mapK :: ([b] -> r) -> (a -> b) -> [a] -> r
mapK k f xs = foldl (\k x xs -> k (f x : xs)) k xs []
For, mapK if you take the k to be id then you get map:
map :: (a -> b) -> [a] -> [b]
map f xs = foldl (\k x xs -> k (f x : xs)) id xs []
Similarly, for filter:
filter :: (a -> Bool) -> [a] -> [a]
filter p xs = foldl (\k x xs -> k (if p x then x : xs else xs)) id xs []
There you have it, tail recursive map and filter functions. However, don't forget that they are actually doing twice the work. In addition, they won't work for infinite lists because the result will not be generated until the end of the list is reached (which will never happen for infinite lists).
I'm suspecting the professor/lecturer is expecting solutions where tail recursion is used "directly", i.e. lexically, within the source code of the function, not indirectly, or "dynamically", where tail recursion only happens at runtime within the scope of some subroutine call.
Otherwise, you might as well supply e.g. Prelude.foldl as the implementation for a custom foldl of yours, since it, possibly, uses tail recursion under the hood, and thus does yours:
import Prelude as P
foldl = P.foldl
but obviously something like that wouldn't be accepted.
Why can you reverse a list with the foldl?
reverse' :: [a] -> [a]
reverse' xs = foldl (\acc x-> x : acc) [] xs
But this one gives me a compile error.
reverse' :: [a] -> [a]
reverse' xs = foldr (\acc x-> x : acc) [] xs
Error
Couldn't match expected type `a' with actual type `[a]'
`a' is a rigid type variable bound by
the type signature for reverse' :: [a] -> [a] at foldl.hs:33:13
Relevant bindings include
x :: [a] (bound at foldl.hs:34:27)
acc :: [a] (bound at foldl.hs:34:23)
xs :: [a] (bound at foldl.hs:34:10)
reverse' :: [a] -> [a] (bound at foldl.hs:34:1)
In the first argument of `(:)', namely `x'
In the expression: x : acc
Every foldl is a foldr.
Let's remember the definitions.
foldr :: (a -> s -> s) -> s -> [a] -> s
foldr f s [] = s
foldr f s (a : as) = f a (foldr f s as)
That's the standard issue one-step iterator for lists. I used to get my students to bang on the tables and chant "What do you do with the empty list? What do you do with a : as"? And that's how you figure out what s and f are, respectively.
If you think about what's happening, you see that foldr effectively computes a big composition of f a functions, then applies that composition to s.
foldr f s [1, 2, 3]
= f 1 . f 2 . f 3 . id $ s
Now, let's check out foldl
foldl :: (t -> a -> t) -> t -> [a] -> t
foldl g t [] = t
foldl g t (a : as) = foldl g (g t a) as
That's also a one-step iteration over a list, but with an accumulator which changes as we go. Let's move it last, so that everything to the left of the list argument stays the same.
flip . foldl :: (t -> a -> t) -> [a] -> t -> t
flip (foldl g) [] t = t
flip (foldl g) (a : as) t = flip (foldl g) as (g t a)
Now we can see the one-step iteration if we move the = one place leftward.
flip . foldl :: (t -> a -> t) -> [a] -> t -> t
flip (foldl g) [] = \ t -> t
flip (foldl g) (a : as) = \ t -> flip (foldl g) as (g t a)
In each case, we compute what we would do if we knew the accumulator, abstracted with \ t ->. For [], we would return t. For a : as, we would process the tail with g t a as the accumulator.
But now we can transform flip (foldl g) into a foldr. Abstract out the recursive call.
flip . foldl :: (t -> a -> t) -> [a] -> t -> t
flip (foldl g) [] = \ t -> t
flip (foldl g) (a : as) = \ t -> s (g t a)
where s = flip (foldl g) as
And now we're good to turn it into a foldr where type s is instantiated with t -> t.
flip . foldl :: (t -> a -> t) -> [a] -> t -> t
flip (foldl g) = foldr (\ a s -> \ t -> s (g t a)) (\ t -> t)
So s says "what as would do with the accumulator" and we give back \ t -> s (g t a) which is "what a : as does with the accumulator". Flip back.
foldl :: (t -> a -> t) -> t -> [a] -> t
foldl g = flip (foldr (\ a s -> \ t -> s (g t a)) (\ t -> t))
Eta-expand.
foldl :: (t -> a -> t) -> t -> [a] -> t
foldl g t as = flip (foldr (\ a s -> \ t -> s (g t a)) (\ t -> t)) t as
Reduce the flip.
foldl :: (t -> a -> t) -> t -> [a] -> t
foldl g t as = foldr (\ a s -> \ t -> s (g t a)) (\ t -> t) as t
So we compute "what we'd do if we knew the accumulator", and then we feed it the initial accumulator.
It's moderately instructive to golf that down a little. We can get rid of \ t ->.
foldl :: (t -> a -> t) -> t -> [a] -> t
foldl g t as = foldr (\ a s -> s . (`g` a)) id as t
Now let me reverse that composition using >>> from Control.Arrow.
foldl :: (t -> a -> t) -> t -> [a] -> t
foldl g t as = foldr (\ a s -> (`g` a) >>> s) id as t
That is, foldl computes a big reverse composition. So, for example, given [1,2,3], we get
foldr (\ a s -> (`g` a) >>> s) id [1,2,3] t
= ((`g` 1) >>> (`g` 2) >>> (`g` 3) >>> id) t
where the "pipeline" feeds its argument in from the left, so we get
((`g` 1) >>> (`g` 2) >>> (`g` 3) >>> id) t
= ((`g` 2) >>> (`g` 3) >>> id) (g t 1)
= ((`g` 3) >>> id) (g (g t 1) 2)
= id (g (g (g t 1) 2) 3)
= g (g (g t 1) 2) 3
and if you take g = flip (:) and t = [] you get
flip (:) (flip (:) (flip (:) [] 1) 2) 3
= flip (:) (flip (:) (1 : []) 2) 3
= flip (:) (2 : 1 : []) 3
= 3 : 2 : 1 : []
= [3, 2, 1]
That is,
reverse as = foldr (\ a s -> (a :) >>> s) id as []
by instantiating the general transformation of foldl to foldr.
For mathochists only. Do cabal install newtype and import Data.Monoid, Data.Foldable and Control.Newtype. Add the tragically missing instance:
instance Newtype (Dual o) o where
pack = Dual
unpack = getDual
Observe that, on the one hand, we can implement foldMap by foldr
foldMap :: Monoid x => (a -> x) -> [a] -> x
foldMap f = foldr (mappend . f) mempty
but also vice versa
foldr :: (a -> b -> b) -> b -> [a] -> b
foldr f = flip (ala' Endo foldMap f)
so that foldr accumulates in the monoid of composing endofunctions, but now to get foldl, we tell foldMap to work in the Dual monoid.
foldl :: (b -> a -> b) -> b -> [a] -> b
foldl g = flip (ala' Endo (ala' Dual foldMap) (flip g))
What is mappend for Dual (Endo b)? Modulo wrapping, it's exactly the reverse composition, >>>.
For a start, the type signatures don't line up:
foldl :: (o -> i -> o) -> o -> [i] -> o
foldr :: (i -> o -> o) -> o -> [i] -> o
So if you swap your argument names:
reverse' xs = foldr (\ x acc -> x : acc) [] xs
Now it compiles. It won't work, but it compiles now.
The thing is, foldl, works from left to right (i.e., backwards), whereas foldr works right to left (i.e., forwards). And that's kind of why foldl lets you reverse a list; it hands you stuff in reverse order.
Having said all that, you can do
reverse' xs = foldr (\ x acc -> acc ++ [x]) [] xs
It'll be really slow, however. (Quadratic complexity rather than linear complexity.)
You can use foldr to reverse a list efficiently (well, most of the time in GHC 7.9—it relies on some compiler optimizations), but it's a little weird:
reverse xs = foldr (\x k -> \acc -> k (x:acc)) id xs []
I wrote an explanation of how this works on the Haskell Wiki.
foldr basically deconstructs a list, in the canonical way: foldr f initial is the same as a function with patterns:(this is basically the definition of foldr)
ff [] = initial
ff (x:xs) = f x $ ff xs
i.e. it un-conses the elements one by one and feeds them to f. Well, if all f does is cons them back again, then you get the list you originally had! (Another way to say that: foldr (:) [] ≡ id.
foldl "deconstructs" the list in inverse order, so if you cons back the elements you get the reverse list. To achieve the same result with foldr, you need to append to the "wrong" end – either as MathematicalOrchid showed, inefficiently with ++, or by using a difference list:
reverse'' :: [a] -> [a]
reverse'' l = dl2list $ foldr (\x accDL -> accDL ++. (x:)) empty l
type DList a = [a]->[a]
(++.) :: DList a -> DList a -> DList a
(++.) = (.)
emptyDL :: DList a
emptyDL = id
dl2list :: DLList a -> [a]
dl2list = ($[])
Which can be compactly written as
reverse''' l = foldr (flip(.) . (:)) id l []
This is what foldl op acc does with a list with, say, 6 elements:
(((((acc `op` x1) `op` x2) `op` x3) `op` x4) `op` x5 ) `op` x6
while foldr op acc does this:
x1 `op` (x2 `op` (x3 `op` (x4 `op` (x5 `op` (x6 `op` acc)))))
When you look at this, it becomes clear that if you want foldl to reverse the list, op should be a "stick the right operand to the beginning of the left operand" operator. Which is just (:) with arguments reversed, i.e.
reverse' = foldl (flip (:)) []
(this is the same as your version but using built-in functions).
When you want foldr to reverse the list, you need a "stick the left operand to the end of the right operand" operator. I don't know of a built-in function that does that; if you want you can write it as flip (++) . return.
reverse'' = foldr (flip (++) . return) []
or if you prefer to write it yourself
reverse'' = foldr (\x acc -> acc ++ [x]) []
This would be slow though.
A slight but significant generalization of several of these answers is that you can implement foldl with foldr, which I think is a clearer way of explaining what's going on in them:
myMap :: (a -> b) -> [a] -> [b]
myMap f = foldr step []
where step a bs = f a : bs
-- To fold from the left, we:
--
-- 1. Map each list element to an *endomorphism* (a function from one
-- type to itself; in this case, the type is `b`);
--
-- 2. Take the "flipped" (left-to-right) composition of these
-- functions;
--
-- 3. Apply the resulting function to the `z` argument.
--
myfoldl :: (b -> a -> b) -> b -> [a] -> b
myfoldl f z as = foldr (flip (.)) id (toEndos f as) z
where
toEndos :: (b -> a -> b) -> [a] -> [b -> b]
toEndos f = myMap (flip f)
myReverse :: [a] -> [a]
myReverse = myfoldl (flip (:)) []
For more explanation of the ideas here, I'd recommend reading Tom Ellis' "What is foldr made of?" and Brent Yorgey's "foldr is made of monoids".
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]
I've been playing around with Haskell a fair bit, including practising writing functions in point-free form. Here is an example function:
dotProduct :: (Num a) => [a] -> [a] -> a
dotProduct xs ys = sum (zipWith (*) xs ys)
I would like to write this function in point-free form. Here is an example I found elsewhere:
dotProduct = (sum .) . zipWith (*)
However, I don't understand why the point-free form looks like (sum .) . zipWith (*) instead of sum . zipWith (*). Why is sum in brackets and have 2 composition operators?
dotProduct xs ys = sum (zipWith (*) xs ys) -- # definition
dotProduct xs = \ys -> sum (zipWith (*) xs ys) -- # f x = g <=> f = \x -> g
= \ys -> (sum . (zipWith (*) xs)) ys -- # f (g x) == (f . g) x
= sum . (zipWith (*) xs) -- # \x -> f x == f
= sum . zipWith (*) xs -- # Precedence rule
dotProduct = \xs -> sum . zipWith (*) xs -- # f x = g <=> f = \x -> g
= \xs -> (sum .) (zipWith (*) xs) -- # f * g == (f *) g
= \xs -> ((sum .) . zipWith (*)) xs -- # f (g x) == (f . g) x
= (sum .) . zipWith (*) -- # \x -> f x == f
The (sum .) is a section. It is defined as
(sum .) f = sum . f
Any binary operators can be written like this, e.g. map (7 -) [1,2,3] == [7-1, 7-2, 7-3].
KennyTM's answer is excellent, but still I'd like to offer another perspective:
dotProduct = (.) (.) (.) sum (zipWith (*))
(.) f g applies f on the result of g given one argument
(.) (.) (.) f g applies f on the result of g given two arguments
(.) (.) ((.) (.) (.)) f g applies f on the result of g given three arguments
...
Can do (.~) = (.) (.) (.), (.~~) = (.) (.) (.~), (.~~~) = (.) (.) (.~~) and now let foo a b c d = [1..5]; (.~~~) sum foo 0 0 0 0 results in 15.
But I wouldn't do it. It will probably make code unreadable. Just be point-full.
Conal's TypeCompose provides a synonym for (.) called result. Perhaps this name is more helpful for understanding what's going on.
fmap also works instead of (.), if importing the relevant instances (import Control.Applicative would do it) but its type is more general and thus perhaps more confusing.
Conal's concept of "fusion" (not to be confused with other usages of "fusion") is kind of related and imho offers a nice way to compose functions. More details in this long Google Tech Talk that Conal gave