Is it possible to implement foldl/foldr using unsided fold? - haskell

By unsided fold, I mean a hypothetic primitive fold operation for associative operators that, does not guarantee any ordering. That is, (fold + 0 [a b c d]) could be (+ (+ a b) (+ c d)) or (+ (+ (+ a b) c) d).
Given that this operation is fusionable, highly paralelizable and universal, I've thought in including it together with map and concat as the only list primitives for my non-recursive minimalist language. I've managed to implement most list functions with it, but not the sided folds foldl/foldrthemselves. Is it possible?

If you have fold and map that is universal. The slogan here is foldr is made of monoids In fact, the standard haskell typeclass Foldable implements foldr and foldl in just this way
The trick is that the set of endomorphisms over a set forms a monoid under function composition with the identity function as the identity.
Note though that foldr and foldl are inherently sequential. So, this trick has to give up any parallelism you have in your implementation of fold and map. Essentially, the encoding of foldr into foldMap is the encoding of a delayed sequential computation into a potentially unordered one. That is why I encourage the use of foldMap over foldr when possible--it supports implicit parallism when that is possible, but is equivalent in expressive power.
EDIT: Putting everything in one place
We define the set of endo morphisms over a
newtype Endo a = Endo { appEndo :: a -> a }
instance Monoid (Endo a) where
mempty = Endo id
Endo f `mappend` Endo g = Endo (f . g)
then in foldable, we see the definition for foldr
foldr f z t = appEndo (foldMap (Endo . f) t) z
this uses foldMap which has type Monoid m => (a -> m) -> t a -> m (where t is the collection we are folding over, we can pretend it is a list from now on giving Monoid m => (a -> m) -> [a] -> m and is equivalent to
foldMap f ls = fold (map f ls)
where fold is the monoid fold. If you have a unordered fold called fold' :: (a -> a -> a) -> a -> [a] -> a then that is just
fold = fold' mappend mempty
so
foldr f z t = appEndo (foldMap (Endo . f) t) z
= appEndo (fold (map (Endo . f) t)) z
= appEndo (fold' mappend mempty (map (Endo . f) t)) z
= appEndo (fold' (\(Endo f) (Endo g) -> Endo (f . g) (Endo id) (map (Endo . f) t)) z
which can be further simplified to
foldr f z t = (fold' (.) id (map f t)) z
and dropping the unecessary parens
foldr f z t = fold' (.) id (map f t) z
which is what Daniel Wagner gave as his answer. You can implement foldl in a similar way, or via foldr.

foldr f z xs = fold (.) id (map f xs) z
For example, in ghci:
*Dmwit Debug.SimpleReflect> let foldr' f z xs = foldb (.) id (map f xs) z
*Dmwit Debug.SimpleReflect> foldr' f z [w,x,y]
f w (f x (f y z))
*Dmwit Debug.SimpleReflect> foldr f z [w,x,y]
f w (f x (f y z))

Related

typclass task Haskell flip and fold

I have this function.
reverse :: (Foldable t, Applicative t, Monoid (t a)) => t a -> t a
reverse ls = foldr (\l r -> r `mappend` pure l) mempty ls`
and now I should turn it into this:
reverse::(foldable t, Applicative t, Monoid (t,a) => t a -> t a)
reverse = foldr (flip _____ . _____ ) ______`
I have no clue, I didn't even know that I can leave out the argument (ls) in the
ls = Foldr ... equation.
So I have no argument for flip and no funktion for the point operator.
Flip need 2 arguments and one funktion, I dont see any argument maybe mempty.
The dot notation needs 2 funktion and one argument. Maybe i am wrong.
reverse::(foldable t, Applicative t, Monoid (t,a) => t a -> t a)
reverse = foldr (flip _____ . _____ ) ______`
Short answer
reverse = foldr (flip mappend . pure) mempty
Long answer
I think you don't understand one main point - you can apply any function to arguments "not right away".
For example, you can write this:
f = (+) 5
So, function (operator) + has type Num a => a -> a -> a, but it doesn't mean that when you want apply + you must use two arguments at once. In the example above you defined new object which is function of adding 5.
One more example:
g = head . tail
Although (.) :: (b -> c) -> (a -> b) -> a -> c, you don't have to pass the last parameter right away. So function g has type [a] -> a.
Lets' return to your task:
(\l r -> r ``mappend`` pure l)
Rewrite this function:
(\l r -> mappend r (pure l))
(\l r -> flip mappend (pure l) r)
(\l -> flip mappend (pure l))
(\l -> (flip mappend . pure) l)
flip mappend . pure
And you can rewrite
reverse ls = foldr (flip mappend . pure) mempty ls
to
reverse = foldr (flip mappend . pure) mempty

Is (fmap f) the same as (f .) if f is a function of type a->b?

I am trying to implement a Functor instance of
data ComplicatedA a b
= Con1 a b
| Con2 [Maybe (a -> b)]
For Con2, my thought process was the fmap needs to be something like
fmap f (Con2 xs) = Con2 (map f' xs)
then I need to have a list map function f' like
Maybe (a -> x) -> Maybe (a -> y)
Since Maybe is a Functor, I can write f' like
fmap ((a->x) -> (a->y))
In order to get ((a->x) -> (a->y)), I thought I could just do
fmap (x->y) which is the same as (fmap f)
So my sulotion was
instance Functor (ComplicatedA a) where
fmap f (Con1 x y) = Con1 x (f y)
fmap f (Con2 xs) = Con2 (map (fmap (fmap f)) xs)
However the real solution uses (f .) instead of (fmap f) to get ((a->x) -> (a->y)) from x -> y and it looks like this
instance Functor (ComplicatedA a) where
fmap f (Con1 a b) = Con1 a (f b)
fmap f (Con2 l) = Con2 (map (fmap (f .)) l)
I was just wondering what the problem was with my thought process and solution. Is (fmap f) the same as (f .) if f is a function of type a->b?
Thank you in advance.
The solutions are indeed equivalent. fmap for the function/reader functor is (.):
instance Functor ((->) r) where
fmap = (.)
((->) r is the function type constructor being used with prefix syntax -- (->) r a is the same as r -> a.)
The intuition is that, as you have noted, (.) :: (x -> y) -> (a -> x) -> (a -> y) uses a x -> y function to modify the results of an a -> x function.

Monoids and Num in Haskell

I have been learning Haskell over the past few months and I came across an example of Monoids that has me puzzled.
Given these definitions:
data Tree a = Empty | Node a (Tree a) (Tree a) deriving (Show, Read, Eq)
instance F.Foldable Tree where
foldMap f Empty = mempty
foldMap f (Node x l r) = F.foldMap f l `mappend`
f x `mappend`
F.foldMap f r
And this Tree:
testTree = Node 5
(Node 3
(Node 1 Empty Empty)
(Node 6 Empty Empty)
)
(Node 9
(Node 8 Empty Empty)
(Node 10 Empty Empty)
)
If I run:
ghci> F.foldl (+) 0 testTree
42
ghci> F.foldl (*) 1 testTree
64800
How does GHCi know what Monoid to use for the mappend when it's folding? Because by default the numbers in the tree are just of the type Num, and we never explicitly said they where of some Monoid such as Sum or Product.
So how does GHCi infer the correct Monoid to use? Or am I totally off at this point?
Example source: http://learnyouahaskell.com/functors-applicative-functors-and-monoids#monoids
Short answer: it is a type constraint in the signature of foldMap.
If we look to the source code of Foldable (more specifically foldMap), we see:
class Foldable (t :: * -> *) where
...
foldMap :: Monoid m => (a -> m) -> t a -> m
So that means that if we declare Tree a member of Foldable (not that Tree has kind * -> *), it means that a foldMap is defined over that tree, such that: foldMap :: Monoid m => (a -> m) -> Tree a -> m. So this thus means that the result type (and the result of the function passed to foldMap) m must be a Monoid.
Haskell is statically typed: after compile time, Haskell knows exactly the types that are passed in an out of every function instance. So that means it knows for instance what the output type will be, and thus how to handle it.
Now Int is not an instance of Monoid. You here use F.foldl (+) 0 testTree, so that means that you more or less have constructed an "ad hoc" monoid. This works if we look at the source code of foldl:
foldl :: (b -> a -> b) -> b -> t a -> b
foldl f z t = appEndo (getDual (foldMap (Dual . Endo . flip f) t)) z
This has a lot of logic surrounding the parameters f, z and t. So let us break that down first.
Let us first take a look at Dual . Endo . flip f. This is short for:
helper = \x -> Dual (Endo (\y -> f y x))
Dual and Endo are types with each one constructor that takes one parameter. So we wrap the outcome of f y x in the Dual (Endo ...) constructors.
We will use this as the function of foldMap. If our f has type a -> b -> a, then this function has type b -> Dual (Endo a). So the output type of the function passed to foldMap has output type Dual (Endo a). Now if we inspect the source code, we see two intersting things:
instance Monoid (Endo a) where
mempty = Endo id
Endo f `mappend` Endo g = Endo (f . g)
instance Monoid a => Monoid (Dual a) where
mempty = Dual mempty
Dual x `mappend` Dual y = Dual (y `mappend` x)
(note that it is y `mappend` x, not x `mappend` y).
So what happens here is that the mempty that is used in the foldMap is mempty = Dual mempty = Dual (Endo id). So a Dual (Endo ...) that encapsulates the identity function.
Furthermore the mappend of two duals comes down to function composition of the values in Endo. So:
mempty = Dual (Endo id)
mappend (Dual (Endo f)) (Dual (Endo g)) = Dual (Endo (g . f))
So that means that if we fold over the tree, in case we see an Empty (a leaf), we will return mempty, and in case we see a Node x l r, we will perform the mappend as described above. So a "specialized" foldMap will look like:
-- specialized foldMap
foldMap f Empty = Dual (Endo id)
foldMap f (Node x l r) = Dual (Endo (c . b . a))
where Dual (Endo a) = foldMap f l
Dual (Endo b) = helper x
Dual (Endo c) = foldMap f l
So for every Node we make a function composition right-to-left over the the children and item of the node. a and c can be compositions of the tree as well (since these are recursive calls). In case of a Leaf, we do nothing (we return id, but the composition over an id is a no-op).
So that means that if we have a tree:
5
|- 3
| |- 1
| `- 6
`- 9
|- 8
`- 10
This will result in a function:
(Dual (Endo ( (\x -> f x 10) .
(\x -> f x 9) .
(\x -> f x 8) .
(\x -> f x 5) .
(\x -> f x 6) .
(\x -> f x 3) .
(\x -> f x 1)
)
)
)
(omitted the identities, to make it a cleaner). This is the outcome of getDual (foldMap (Dual . Endo . flip f)). But now we need to post process this result. With getDual, we get the content wrapped in the Dual constructor. So now we have:
Endo ( (\x -> f x 10) .
(\x -> f x 9) .
(\x -> f x 8) .
(\x -> f x 5) .
(\x -> f x 6) .
(\x -> f x 3) .
(\x -> f x 1)
)
and with appEndo, we obtain the function wrapped in Endo, so:
( (\x -> f x 10) .
(\x -> f x 9) .
(\x -> f x 8) .
(\x -> f x 5) .
(\x -> f x 6) .
(\x -> f x 3) .
(\x -> f x 1)
)
and then we apply this to z the "initial" value. So that means that we will process the chain starting with z (the initial element), and apply it like:
f (f (f (f (f (f (f z 1) 3) 6) 5) 8) 9) 10
So we have constructed some sort of Monoid, where mappend is replaced by f, and mempty as a no-op (the identity function).
It doesn't need to. foldl is translated into foldr which translates into foldMap over Endo which means function composition which means simple nesting of the function you have supplied.
Or something. Meaning, foldl could be translated into foldMap over Dual . Endo which composes left-to-right, etc..
update: yes, the docs says:
Foldable instances are expected to satisfy the following laws:
foldr f z t = appEndo (foldMap (Endo . f) t ) z
foldl f z t = appEndo (getDual (foldMap (Dual . Endo . flip f) t)) z -- << --
fold = foldMap id
Dual (Endo f) <> Dual (Endo g) = Dual (Endo g <> Endo f) = Dual (Endo (g . f)). So when appEndo strikes, the chain of functions that's been built, i.e.
((+10) . (+9) . (+8) . (+5) . ... . (+1))
or equivalent (here, shown for the (+) case), is applied to the user-supplied value -- in your case,
0
Another thing to notice is that Endo and Dual are newtypes, so all these machinations will be done by compiler, and be gone by run time.
There is (implicitly, if not explicitly), a monoid instance for ordinary functions of the form a -> a, where mappend corresponds to function composition, and mempty corresponds to the id function.
What is (+)? It is a function (Num a) => a -> a -> a. If you foldMap over your foldable full of numbers with +, you turn each one into the partially applied (+ <some number), which is a a -> a. Lo and behold, you've found the magic f that will turn everything in your foldable into a monoid!
Assuming there was a direct monoid instance for functions, you'd be able to do:
foldMap (+) [1, 2, 3, 4]
, which would produce an ultimate (Num a) => a -> a that you could apply to 0 to get 10.
There is no such direct instance however, so you need to use the builtin newtype wrapper Endo and the corresponding unwrapper appEndo, which implement monoid for a -> a functions. Here's what it looks like:
Prelude Data.Monoid> (appEndo (foldMap (Endo . (+)) [1, 2, 3, 4])) 0
10
Here Endo . is just our annoying need to lift plain a -> as so they have their natural Monoid instance. After the foldMap is done reducing our foldable by turning everything into a -> as and chaining them together with composition, we extract the final a -> a using appEndo, and finally apply it to 0.

Is it possible to fold <*> in haskell?

I want to realize something like
fun1 f a_ziplist
for example
getZipList $ (\x y z -> x*y+z) <$> ZipList [4,7] <*> ZipList [6,9] <*> ZipList [5,10]
f = (\x y z -> x*y+z)
ziplist = [[4,7],[6,9],[5,10]]
To do this, I want to recursively apply <*> like
foldx (h:w) = h <*> foldx w
foldx (w:[]) = w
but it seems impossible to make <*> recursive.
Let's play with the types in ghci, to see where they carry us.
λ import Control.Applicative
The type of (<*>)
λ :t (<*>)
(<*>) :: Applicative f => f (a -> b) -> f a -> f b
The type of foldr:
λ :t Prelude.foldr
Prelude.foldr :: (a -> b -> b) -> b -> [a] -> b
Perhaps we could use (<*>) as the function that is passed as the first parameter of foldr. What would be the type?
λ :t Prelude.foldr (<*>)
Prelude.foldr (<*>) :: Applicative f => f a -> [f (a -> a)] -> f a
So it seems that it takes an initial value in an applicative context, and a list of functions in an applicative context, and returns another applicative.
For example, using ZipList as the applicative:
λ getZipList $ Prelude.foldr (<*>) (ZipList [2,3]) [ ZipList [succ,pred], ZipList [(*2)] ]
The result is:
[5]
I'm not sure if this is what the question intended, but it seems like a natural way to fold using (<*>).
If the ziplist argument has to be a plain list, it looks impossible. This is because fold f [a1,...,an] must be well typed for every n, hence f must be a function type taking at least n arguments for every n, hence infinitely many.
However, if you use a GADT list type, in which values expose their length as a type-level natural you can achieve something similar to what you want.
{-# LANGUAGE DataKinds, KindSignatures, TypeFamilies, GADTs #-}
import Control.Applicative
-- | Type-level naturals
data Nat = Z | S Nat
-- | Type family for n-ary functions
type family Fn (n :: Nat) a b
type instance Fn Z a b = b
type instance Fn (S n) a b = a -> Fn n a b
-- | Lists exposing their length in their type
data List a (n :: Nat) where
Nil :: List a Z
Cons :: a -> List a n -> List a (S n)
-- | General <*> applied to a list of arguments of the right length
class Apply (n :: Nat) where
foldF :: Applicative f => f (Fn n a b) -> List (f a) n -> f b
instance Apply Z where
foldF f0 Nil = f0
instance Apply n => Apply (S n) where
foldF fn (Cons x xs) = foldF (fn <*> x) xs
test :: [(Integer,Integer,Integer)]
test = foldF (pure (,,)) (Cons [10,11] (Cons [20,21] (Cons [30,31] Nil)))
-- Result: [(10,20,30),(10,20,31),(10,21,30),(10,21,31)
-- ,(11,20,30),(11,20,31),(11,21,30),(11,21,31)]
In general folding (<*>) is tricky because of types, as others have mentioned. But for your specific example, where your ziplist elements are all of the same type, you can use a different method and make your calculation work with a small change to f to make it take a list argument instead of single elements:
import Data.Traversable
import Control.Applicative
f = (\[x,y,z] -> x*y+z)
ziplist = [[4,7],[6,9],[5,10]]
fun1 f l = getZipList $ f <$> traverse ZipList l
It's even possible to achieve this with just Data.List and Prelude functions:
fun1 f = map f . transpose
To do this, I want to recursively apply <*> like
foldx (h:w) = h <*> foldx w
foldx (w:[]) = w
but it seems impossible to make <*> recursive.
I think you're getting confused over left- vs. right-associativity. danidiaz reformulates this in terms of foldr (<*>), which is quite useful for this analysis. The documentation gives a useful definition of foldr in terms of expansion:
foldr f z [x1, x2, ..., xn] == x1 `f` (x2 `f` ... (xn `f` z) ...)
So applying that to your case:
foldr (<*>) z [x1, x2, ..., xn] == x1 <*> (x2 <*> ... (xn <*> z) ...)
Note the parens. <*> is left-associative, so the foldr expansion is different from:
x1 <*> x2 <*> ... <*> xn <*> z == ((... (x1 <*> x2) <*> ...) <*> xn) <*> z
Let's think also a bit more about what foldr (<*>) means. Another way of thinking of this is to rewrite it just slightly:
flip (foldr (<*>)) :: Applicative f :: [f (a -> a)] -> f a -> f a
Types of the form (a -> a) are often called endomorphisms, and they form a monoid, with composition as the operation and id as the identity. There's a newtype wrapper in Data.Monoid for these:
newtype Endo a = Endo { appEndo :: a -> a }
instance Monoid (Endo a) where
mempty = id
mappend = (.)
This gives us yet another way to think of foldr (<*>), by formulating it in terms of Endo:
toEndo :: Applicative f => f (a -> a) -> Endo (f a)
toEndo ff = Endo (ff <*>)
And then what foldr (<*>) does, basically, is reduce this monoid:
foldrStar :: Applicative f => [f (a -> a)] -> Endo (f a)
foldrStar fs = mconcat $ map toMonoid fs
what you have is equivalent to zipWith3 (\x y z -> x*y+z) [4,7] [6,9] [5,10].
it's impossible to foldl the <*> (and you do need foldl as <*> associates to the left) because foldl :: (a -> b -> a) -> a -> [b] -> a i.e. it's the same a in a -> b -> a, but when you apply your ternary function on first list of numbers, you get a list of binary functions, and then unary functions at the next step, and only finally, numbers (all different types, then):
>> let xs = map ZipList [[4,7],[6,9],[5,10]]
>> getZipList $ pure (\x y z -> x*y+z) <*> (xs!!0) <*> (xs!!1) <*> (xs!!2)
[29,73]
>> foldl (<*>) (pure (\x y z -> x*y+z)) xs
<interactive>:1:6:
Occurs check: cannot construct the infinite type: b = a -> b
Expected type: f (a -> b)
Inferred type: f b
In the first argument of `foldl', namely `(<*>)'
In the expression: foldl (<*>) (pure (\ x y z -> x * y + z)) xs
>> :t foldl
foldl :: ( a -> b -> a ) -> a -> [b] -> a
>> :t (<*>)
(<*>) :: (Applicative f) => f (a -> b) -> f a -> f b -- f (a -> b) = f b
The answer by chi addresses this, but the arity is fixed (for a particular code). In effect, what that answer really does is defining (a restricted version of) zipWithN (well, here, when used with the ZipList applicative - obviously, it works with any applicative in general) for any N (but just for the a -> a -> a -> ... -> a type of functions), whereas e.g.
zipWith7 :: (a -> b -> c -> d -> e -> f -> g -> h) ->
[a] -> [b] -> [c] -> [d] -> [e] -> [f] -> [g] -> [h]
(in other words, zipWith3 (,,) [10,11] ([20,21]::[Integer]) ([30,31]::[Int]) works).

What is a general scheme for writing a function in pointfree style?

I am working through the 20 Intermediate Haskell Exercises at the moment, which is quite a fun exercise. It involves implementing various instances of the typeclasses Functor and Monad (and functions that takes Functors and Monads as arguments) but with cute names like Furry and Misty to disguise what we're doing (makes for some interesting code).
I've been trying to do some of this in a point-free style, and I wondered if there's a general scheme for turning a point-ful (?) definition into a point-free definition. For example, here is the typeclass for Misty:
class Misty m where
unicorn :: a -> m a
banana :: (a -> m b) -> m a -> m b
(the functions unicorn and banana are return and >>=, in case it's not obvious) and here's my implementation of apple (equivalent to flip ap):
apple :: (Misty m) => m a -> m (a -> b) -> m b
apple x f = banana (\g -> banana (unicorn . g) x) f
Later parts of the exercises have you implement versions of liftM, liftM2 etc. Here are my solutions:
appleTurnover :: (Misty m) => m (a -> b) -> m a -> m b
appleTurnover = flip apple
banana1 :: (Misty m) => (a -> b) -> m a -> m b
banana1 = appleTurnover . unicorn
banana2 :: (Misty m) => (a -> b -> c) -> m a -> m b -> m c
banana2 f = appleTurnover . banana1 f
banana3 :: (Misty m) => (a -> b -> c -> d) -> m a -> m b -> m c -> m d
banana3 f x = appleTurnover . banana2 f x
banana4 :: (Misty m) => (a -> b -> c -> d -> e) -> m a -> m b -> m c -> m d -> m e
banana4 f x y = appleTurnover . banana3 f x y
Now, banana1 (equivalent to liftM or fmap) I was able to implement in pointfree style, by a suitable definition of appleTurnover. But with the other three functions I've had to use parameters.
My question is: is there a recipe for turning definitions like these into point-free definitions?
As demonstrated by the pointfree utility, it's possible to do any such conversion automatically. However, the result is more often obfuscated than improved. If one's goal is to enhance legibility rather than destroy it, then the first goal should be to identify why an expression has a particular structure, find a suitable abstraction, and build things up that way.
The simplest structure is simply chaining things together in a linear pipeline, which is plain function composition. This gets us pretty far just on its own, but as you noticed it doesn't handle everything.
One generalization is to functions with additional arguments, which can be built up incrementally. Here's one example: Define onResult = (. (.)). Now, applying onResult n times to an initial value of id gives you function composition with the result of an n-ary function. So we can define comp2 = onResult (.), and then write comp2 not (&&) to define a NAND operation.
Another generalization--which encompasses the above, really--is to define operators that apply a function to a component of a larger value. An example here would be first and second in Control.Arrow, which work on 2-tuples. Conal Elliott's Semantic Editor Combinators are based on this approach.
A slightly different case is when you have a multi-argument function on some type b, and a function a -> b, and need to combine them into a multi-argument function using a. For the common case of 2-ary functions, the module Data.Function provides the on combinator, which you can use to write expressions like compare `on` fst to compare 2-tuples on their first elements.
It's a trickier issue when a single argument is used more than once, but there are meaningful recurring patterns here that can also be extracted. A common case here is applying multiple functions to a single argument, then collecting the results with another function. This happens to correspond to the Applicative instance for functions, which lets us write expressions like (&&) <$> (> 3) <*> (< 9) to check if a number falls in a given range.
The important thing, if you want to use any of this in actual code, is to think about what the expression means and how that's reflected in the structure. If you do that, and then refactor it into pointfree style using meaningful combinators, you'll often make the intent of the code clearer than it would otherwise be, unlike the typical output of pointfree.
Yes! One of the tricks is to write your dots in prefix notation rather than infix. Then you should be able to find new things that look like function composition. Here's an example:
banana2 f = appleTurnover . banana1 f
= (.) appleTurnover (banana1 f)
= ((.) appleTurnOver) . banana1 $ f
banana2 = (appleTurnover .) . banana1
The source code for the pointfree utility contains more, but this one handles a lot of cases.
banana4 f x y = appleTurnover . banana3 f x y
= (.) appleTurnover ((banana3 f x) y)
= ((.) appleTurnover) . (banana3 f x) $ y
banana4 f x = ((.) appleTurnover) . (banana3 f x)
= (.) ((.) appleTurnover) (banana3 f x)
= ((.) ((.) appleTurnover)) ((banana3 f) x)
= ((.) ((.) appleTurnover)) . (banana3 f) $ x
banana4 f = ((.) ((.) appleTurnover)) . (banana3 f)
= (.) ((.) ((.) appleTurnover)) (banana3 f)
= ((.) ((.) ((.) appleTurnover))) (banana3 f)
= ((.) ((.) ((.) appleTurnover))) . banana3 $ f
banana4 = ((.) ((.) ((.) appleTurnover))) . banana3
= (((appleTurnover .) .) .) . banana3
I use the following term rewrite system:
\x -> f x ------> f
f y x ----------> flip f x y
\x -> f (g x) --> f . g
It is incomplete (read why in books about combinatory logic), but it's enough:
Here is banana2:
banana2 f = appleTurnover . banana1 f
Rewrite as a lambda:
banana2 = \f -> appleTurnover . banana1 f
Write (.) in prefix style:
banana2 = \f -> (.) appleTurnover (banana1 f)
Note that
banana2 = \f -> ((.) appleTurnover) (banana1 f)
So rule 3 can be applied. f is (.) appleTurnover and g is banana:
banana2 = ((.) appleTurnover) . banana1
There is a pointfree package which takes a Haskell function definition and attempts to re-write it in a pointfree style. I'd suggest experimenting with it to get new ideas. See this page for more details; the package is available here.
Since pointfree style is combinators style, just apply known combinators definitions, reading them backwards to make the substitution:
B f g x = f (g x) -- (.) , <$> for ((->) a)
C f x y = f y x -- flip
K x y = x -- const
I x = x -- id
S f g x = f x (g x) -- <*> , ap for ((->) a)
W f x = f x x -- join
(f >>= g) x = g (f x) x
(f =<< g) x = f (g x) x
At times liftMx, liftAx, sequence, sequenceA can simplify things. I'd also consider foldr, unfoldr, iterate, until etc. as basic combinators.
Often, using operator sections helps too:
op a b = (a `op` b) = (`op` b) a = (a `op`) b
Some patterns can become familiar and so, used directly:
((f .) . g) x y = f (g x y)
((. f) . g) x y = g x (f y)
(((f .) .) . g) x y z = (f .) (g x y) z = f (g x y z)
(((. f) .) . g) x y z = (. f) (g x y) z = g x y (f z)
etc.

Resources