How does this piece of obfuscated Haskell code work? - haskell

While reading https://en.uncyclopedia.co/wiki/Haskell (and ignoring all the "offensive" stuff), I stumbled upon the following piece of obfuscated code:
fix$(<$>)<$>(:)<*>((<$>((:[{- thor's mother -}])<$>))(=<<)<$>(*)<$>(*2))$1
When I run that piece of code in ghci (after importing Data.Function and Control.Applicative), ghci prints the list of all powers of 2.
How does this piece of code work?

To begin with, we have the lovely definition
x = 1 : map (2*) x
which by itself is a bit mind-bending if you've never seen it before. Anyway it's a fairly standard trick of laziness and recursion. Now, we'll get rid of the explicit recursion using fix, and point-free-ify.
x = fix (\vs -> 1 : map (2*) vs)
x = fix ((1:) . map (2*))
The next thing we're going to do is expand the : section and make the map needlessly complex.
x = fix ((:) 1 . (map . (*) . (*2)) 1)
Well, now we have two copies of that constant 1. That will never do, so we'll use the reader applicative to de-duplicate that. Also, function composition is a bit rubbish, so let's replace that with (<$>) wherever we can.
x = fix (liftA2 (.) (:) (map . (*) . (*2)) 1)
x = fix (((.) <$> (:) <*> (map . (*) . (*2))) 1)
x = fix (((<$>) <$> (:) <*> (map <$> (*) <$> (*2))) 1)
Next up: that call to map is much too readable. But there's nothing to fear: we can use the monad laws to expand it a bit. In particular, fmap f x = x >>= return . f, so
map f x = x >>= return . f
map f x = ((:[]) <$> f) =<< x
We can point-free-ify, replace (.) with (<$>), and then add some spurious sections:
map = (=<<) . ((:[]) <$>)
map = (=<<) <$> ((:[]) <$>)
map = (<$> ((:[]) <$>)) (=<<)
Substituting this equation in our previous step:
x = fix (((<$>) <$> (:) <*> ((<$> ((:[]) <$>)) (=<<) <$> (*) <$> (*2))) 1)
Finally, you break your spacebar and produce the wonderful final equation
x=fix(((<$>)<$>(:)<*>((<$>((:[])<$>))(=<<)<$>(*)<$>(*2)))1)

Was writing a long answer with a full run-through of my IRC logs of the experiments leading up to the final code (this was in early 2008), but I accidentally all the text :) Not that much of a loss though - for the most part Daniel's analysis is spot on.
Here's what I started with:
Jan 25 23:47:23 <olsner> #pl let q = 2 : map (2*) q in q
Jan 25 23:47:23 <lambdabot> fix ((2 :) . map (2 *))
The differences mostly come down to the order in which the refactorings happened.
Instead of x = 1 : map (2*) x I started with 2 : map ..., and I kept that initial 2 right up until the very last version, where I squeezed in a (*2) and changed the $2 at the end into $1. The "make the map needlessly complex" step didn't happen (that early).
I used liftM2 instead of liftA2
The obfuscated map function was put in before replacing liftM2 with Applicative combinators. That's also when all the spaces disappeared.
Even my "final" version had lots of . for function composition left over. Replacing all of those with <$> apparently happened some time in the months between that and uncyclopedia.
BTW, here's an updated version that no longer mentions the number 2:
fix$(<$>)<$>(:)<*>((<$>((:[{- Jörð -}])<$>))(=<<)<$>(*)<$>(>>=)(+)($))$1

Both answers derive the obfuscated code snippet from the short original given out of the blue, but the question actually asks how does the long obfuscated code do its job.
Here's how:
fix$(<$>)<$>(:)<*>((<$>((:[{- thor's mother -}])<$>))(=<<)<$>(*)<$>(*2))$1
= {- add spaces, remove comment -}
fix $ (<$>) <$> (:) <*> ( (<$> ((:[]) <$>) ) (=<<) <$> (*) <$> (*2) ) $ 1
-- \__\______________/_____________________________/
= {- A <$> B <*> C $ 1 = A (B 1) (C 1) -}
fix $ (<$>) (1 :) ( ( (<$> ((:[]) <$>) ) (=<<) <$> (*) <$> (*2) ) 1 )
-- \__\______________/____________________________/
= {- (<$>) A B = (A <$> B) ; (<$> B) A = (A <$> B) -}
fix $ (1 :) <$> ( (((=<<) <$> ((:[]) <$>) ) <$> (*) <$> (*2) ) 1 )
-- \\____________________/____________________________/
= {- <$> is left associative anyway -}
fix $ (1 :) <$> ( ( (=<<) <$> ((:[]) <$>) <$> (*) <$> (*2) ) 1 )
-- \__________________________________________________/
= {- A <$> foo = A . foo when foo is a function -}
fix $ (1 :) <$> ( ( (=<<) <$> ((:[]) <$>) . (*) . (*2) ) 1 )
-- \__________________________________________________/
= {- ((:[]) <$>) = (<$>) (:[]) = fmap (:[]) is a function -}
fix $ (1 :) <$> ( ( (=<<) . ((:[]) <$>) . (*) . (*2) ) 1 )
-- \__________________________________________________/
= {- ( A . B . C . D) 1 = A (B (C (D 1))) -}
fix $ (1 :) <$> (=<<) ( ((:[]) <$>) ( (*) ( (*2) 1 )))
= {- (*2) 1 = (1*2) = 2 -}
fix $ (1 :) <$> (=<<) ( ((:[]) <$>) ( (*) 2 ))
= {- (*) 2 = (2*) -}
fix $ (1 :) <$> (=<<) ( ((:[]) <$>) (2*) )
= {- ( A <$>) B = A <$> B -}
fix $ (1 :) <$> (=<<) ( (:[]) <$> (2*) )
= {- A <$> foo = A . foo when foo is a function -}
fix $ (1 :) <$> (=<<) ( (:[]) . (2*) )
= {- (f . g) = (\ x -> f (g x)) -}
fix $ (1 :) <$> (=<<) (\ x -> [2*x] )
= {- (=<<) A = ( A =<<) -}
fix $ (1 :) <$> ( (\ x -> [2*x] ) =<<)
Here ( (\ x -> [2*x]) =<<) = (>>= (\ x -> [2*x])) = concatMap (\ x -> [2*x]) = map (2*) is a function, so again, <$> = .:
=
fix $ (1 :) . map (2*)
= {- substitute the definition of fix -}
let xs = (1 :) . map (2*) $ xs in xs
=
let xs = 1 : [ 2*x | x <- xs] in xs
= {- xs = 1 : ys -}
let ys = [ 2*x | x <- 1:ys] in 1:ys
= {- ys = 2 : zs -}
let zs = [ 2*x | x <- 2:zs] in 1:2:zs
= {- zs = 4 : ws -}
let ws = [ 2*x | x <- 4:ws] in 1:2:4:ws
=
iterate (2*) 1
=
[2^n | n <- [0..]]
are all the powers of 2, in increasing order.
This uses
A <$> B <*> C $ x = liftA2 A B C x and since liftA2 A B C is applied to x it's a function, an as a function it means liftA2 A B C x = A (B x) (C x).
(f `op` g) = op f g = (f `op`) g = (`op` g) f are the three laws of operator sections
>>= is monadic bind, and since (`op` g) f = op f g and the types are
(>>=) :: Monad m => m a -> (a -> m b ) -> m b
(\ x -> [2*x]) :: Num t => t -> [ t]
(>>= (\ x -> [2*x])) :: Num t => [ t] -> [ t]
by type application and substitution we see that the monad in question is [] for which (>>= g) = concatMap g.
concatMap (\ x -> [2*x]) xs is simplified as
concat $ map (\ x -> [2*x])
=
concat $ [ [2*x] | x <- xs]
=
[ 2*x | x <- xs]
=
map (\ x -> 2*x )
and by definition,
(f . g) x = f (g x)
fix f = let x = f x in x
iterate f x = x : iterate f (f x)
= x : let y = f x in
y : iterate f (f y)
= x : let y = f x in
y : let z = f y in
z : iterate f (f z)
= ...
= [ (f^n) x | n <- [0..]]
where
f^n = f . f . ... . f
-- \_____n_times _______/
so that
((2*)^n) 1 = ((2*) . (2*) . ... . (2*)) 1
= 2* ( 2* ( ... ( 2* 1 )...))
= 2^n , for n in [0..]

Related

How to implement uncurry point-free in Haskell without app?

I have been wondering how different standard Haskell functions could be implemented point-free. Currently, I am interested in uncurry and I feel this one is quite non-trivial.
The main problem is that we are unable (or as it seems to me) to group the arguments. If we had uncurry (in fact, uncurry ($) would suffice) in use, the solution would have been quite simple:
Make a tuple (f, (x, y)).
Apply assoc1 :: (a, (b, c)) -> ((a, b), c) to the tuple and get ((f, x), y).
Apply the uncurried ($) to the first element of the pair and get (f x, y).
Apply the uncurried ($) to the pair itself and get f x y.
Without the uncurried ($) we would have to extract both elements of the pair separately. E.g.:
uncurry f pair = f (fst pair) (snd pair)
I do not reckon this to be a smooth way to implement something point-free.
In fact, we have got this uncurried ($) at our behest: Control.Arrow.apply (other useful for the solution combinators could also be imported from Control.Arrow). Therefore:
import Control.Arrow ((>>>), (&&&), first, app)
myUncurry = let myAssoc1 = (fst &&& (fst . snd)) &&& (snd . snd)
in (,) >>> (>>> myAssoc1 >>> first app >>> app)
Yet, this feels a small bit like cheating.
Are there any other approaches towards this problem which do not require anything like app?
join on functions gives you (a -> a -> b) -> a -> b, so:
myUncurry f = join (\x y -> f (fst x) (snd y))
myUncurry f = join (\x -> f (fst x) . snd)
myUncurry f = join ((.snd) . f . fst)
myUncurry f = join ((.fst) ((.snd) . f))
myUncurry f = join ((.fst) ((.) (.snd) f))
myUncurry = join . (.fst) . \f -> (.) (.snd) f
myUncurry = join . (.fst) . ((.snd).)
join . (.fst) . ((.snd).) is very readable indeed
The artless, mechanical solution, by "pushing lambdas inward".
uncurry f (x,y) = f x y
uncurry f p = f (fst p) (snd p)
uncurry f = \p -> f (fst p) (snd p)
uncurry f = (<*>) (\p -> f (fst p)) (\p -> snd p)
uncurry f = (<*>) (f . fst) snd
uncurry = \f -> (<*>) (f . fst) snd
uncurry = flip (\f -> (<*>) (f . fst)) snd
uncurry = flip ((<*>) . (\f -> f . fst)) snd
uncurry = flip ((<*>) . (. fst)) snd
With Lambda Calculus' S combinator, Sabc = (a <*> b) c = a c $ b c,
uncurry f (x,y) = f (fst (x,y)) (snd (x,y))
= (f . fst <*> snd) (x,y)
uncurry f = (<*> snd) (f . fst)
= (<*> snd) . (. fst) $ f
hence,
uncurry :: (a -> b -> c) -> (a, b) -> c
uncurry = (<*> snd) . (. fst)
(edit:)
Still it's much more readable (and somewhat elucidating) with one explicit argument left there, as seen above:
uncurry f = f . fst <*> snd
But then this variant, shown by Jon Purdy in the comments,
uncurry f = liftA2 f fst snd
just might be the clearest.
This is because for functions, the monad and the applicative are equivalent in power,
(k =<< f) x = k (f x) x = flip k x (f x) = (flip k <*> f) x
-- i.e., uncurry f = flip (f . fst) =<< snd
and liftA2 f fst snd means, by definition,
= [ f a b | a <- fst ; b <- snd ]
=
do { a <- fst ;
b <- snd ;
return (f a b)
}
= \x -> let
{ a = fst x ;
b = snd x ;
}
in const (f a b) x
(the first one written with Monad Comprehensions). Thus,
uncurry f x = liftA2 f fst snd x
= let
{ a = fst x ;
b = snd x ;
}
in f a b
=
f (fst x) (snd x)
=
(f . fst <*> snd) x
=
(flip (f . fst) =<< snd) x
=
flip (f . fst) (snd x) x
=
(flip (f . fst) . snd) x x
=
join (flip (f . fst) . snd) x
=
join (flip (f . fst) <$> snd) x
following the well known equivalence, k =<< m = join (fmap k m) (and for functions, (<$>) = fmap = (.)).
So we've found yet another expression here,
uncurry f x = join (flip (f . fst) . snd)
= liftA2 f fst snd
= f . fst <*> snd
= flip (f . fst) =<< snd
The liftA2 one just might be the clearest and the least noisy.

How does "(\x y z -> [x,y,z]) <$> (+3) <*> (*2) <*> (/2) $ 5 " really work? [duplicate]

This question already has answers here:
Applicative functor evaluation is not clear to me
(4 answers)
Closed 3 years ago.
In this example from Learn you a Haskell, the author showed how to declare an instance of Applicative for function ->r
instance Applicative ((->) r) where
pure x = (\_ -> x)
f <*> g = \x -> f x (g x)
Later on, he gave a concrete example
ghci> (\x y z -> [x,y,z]) <$> (+3) <*> (*2) <*> (/2) $ 5
[8.0,10.0,2.5]
I try to reason this snippet step by step
Seeing (+3) <*> (*2) <*> (/2) as f <*> g <*> h and thinking that f <*> g <*> h returns a function \x -> f x (g x (h x)), I thought (+3) <*> (*2) <*> (/2) returns a function \x -> x+3 (x*2 (x/2)). But I can not do :t (+3) <*> (*2) <*> (/2) in ghci, which raises this error
error:
• Occurs check: cannot construct the infinite type:
a1 ~ a -> a1 -> b
Expected type: (a -> a1 -> b) -> a1
Actual type: a1 -> a1
• In the second argument of ‘(<*>)’, namely ‘(/ 2)’
In the expression: (+ 3) <*> (* 2) <*> (/ 2)
If (+3) <*> (*2) <*> (/2) does return a function \x -> x+3 (x*2 (x/2)), how does it pattern match with the lambda function \x y z -> [x,y,z]? (Actually I am still having some trouble to understand lambda functions except for the really basic simple ones.)
I think you got the precedence wrong. The expression
(\x y z -> [x,y,z]) <$> (+3) <*> (*2) <*> (/2) $ 5
does not involve as a subexpression the one you mention
(+3) <*> (*2) <*> (/2)
since it should be parsed associating to the left, as follows:
((((\x y z -> [x,y,z]) <$> (+3)) <*> (*2)) <*> (/2)) $ 5
Here, we start to simplify from the left:
(\x y z -> [x,y,z]) <$> (+3)
= { def. <$> }
(\a y z -> [(+3) a,y,z])
Then, we proceed:
(\a y z -> [(+3) a,y,z]) <*> (*2)
= { def. <*> }
(\a z -> [(+3) a, (*2) a,z])
Finally,
(\a z -> [(+3) a, (*2) a,z]) <*> (/2)
= { def. <*> }
(\a -> [(+3) a, (*2) a, (/2) a])
As a last step, we let a to be 5, obtaining [5+3, 5*2, 5/2].
That expression is actually parenthesized to the left:
(\x y z -> [x,y,z]) <$> (+3) <*> (*2) <*> (/2) $ 5
=
((((\x y z -> [x,y,z]) <$> (+3)) <*> (*2)) <*> (/2)) 5
=
((((\x y z -> [x,y,z]) . (+3)) <*> (*2)) <*> (/2)) 5
=
(((\x y z -> [x,y,z]) . (+3)) <*> (*2)) 5 ((/2) 5)
=
((\x y z -> [x,y,z]) . (+3)) 5 ((*2) 5) ((/2) 5)
=
(\x y z -> [x,y,z]) ((+3) 5) ((*2) 5) ((/2) 5)
=
(\x y z -> [x,y,z]) (5+3) (5*2) (5/2)
which is
=
let x = (5+3) in (\y z -> [x,y,z]) (5*2) (5/2)
=
let x = (5+3) ; y = (5*2) in (\z -> [x,y,z]) (5/2)
=
let x = (5+3) ; y = (5*2) ; z = (5/2) in [x,y,z]

How to rewrite in point-free style with a repeating variable?

How to rewrite the following expression in point-free style?
p x y = x*x + y
Using the lambda-calculus I did the following:
p = \x -> \y -> (+) ((*) x x) y
= \x -> (+) ((*) x x) -- here start my problem
= \x -> ((+) . ((*) x )) x
... ?
I asked lambdabot
<Iceland_jack> #pl p x y = x*x + y
<lambdabot> p = (+) . join (*)
join is from Control.Monad and normally has this type
join :: Monad m => m (m a) -> m a
but using instance Monad ((->) x) (if we could left section types this could be written (x ->)) we get the following type / definition
join :: (x -> x -> a) -> (x -> a)
join f x = f x x
Let's ask GHCi to confirm the type:
>> import Control.Monad
>> :set -XTypeApplications
>> :t join #((->) _)
join #((->) _) :: (x -> x -> a) -> x -> a
Since you mentioned Lambda Calculus I will suggest how to solve this with SK combinators. η-reduction was a good try, but as you can tell you can't η-reduce when the variable is used twice.
S = λfgx.fx(gx)
K = λxy.x
The feature of duplication is encoded by S. You simplified your problem to:
λx.(+)((*)xx)
So let us start there. Any lambda term can be algorithmically transformed to a SK term.
T[λx.(+)((*)xx)]
= S(T[λx.(+)])(T[λx.(*)xx]) -- rule 6
= S(K(T[(+)]))(T[λx.(*)xx]) -- rule 3
= S(K(+))(T[λx.(*)xx]) -- rule 1
= S(K(+))(S(T[λx.(*)x])(T[λx.x])) -- rule 6
= S(K(+))(S(*)(T[λx.x])) -- η-reduce
= S(K(+))(S(*)I) -- rule 4
In Haskell, S = (<*>) and K = pure and I = id. Therefore:
= (<*>)(pure(+))((<*>)(*)id)
And rewriting:
= pure (+) <*> ((*) <*> id)
Then we can apply other definitions we know:
= fmap (+) ((*) <*> id) -- pure f <*> x = fmap f x
= fmap (+) (join (*)) -- (<*> id) = join for Monad ((->)a)
= (+) . join (*) -- fmap = (.) for Functor ((->)a)
If you go to http://pointfree.io/
For
p x y = x*x + y
It gives you
p = (+) . join (*)
Just for fun, you can use the State monad to write
p = (+) . uncurry (*) . runState get
runState get simply produces a pair (x, x) from an initial x; get copies the state to the result, and runState returns both the state and that result.
uncurry (*) takes a pair of values rather than 2 separate values ((uncurry (*)) (3, 3) == (*) 3 3 == 9).

applicative rewrite (Haskell)

When I don't grasp how an expression in Haskell works I often find it helps to decompose it into a more basic form.
Using the following definitions
sequenceA :: (Applicative f) => [f a] -> f [a]
sequenceA [] = pure []
sequenceA (x:xs) = (:) <$> x <*> sequenceA xs
instance Applicative ((->) r) where
pure x = (\_ -> x)
f <*> g = \x -> f x (g x)
I rewrote sequenceA [(+3),(+2)] 3 as
(\_ -> (:)) <*> (+3) <*> ((\_ -> (:)) <*> (+2) <*> (\_-> [])) $ 3
And then turned it into (please excuse the format; I'm not sure what the convention is for splitting lines)
(\d ->(\c->(\b -> (\a -> (\_ -> (:)) a (+3) a) b (\_ -> (:)) b) c (+2) c) d (\_ -> []) d) 3
This seems right when I work through it by hand, but I can't get GHCi to accept it. What have I done wrong here? My second question is how to convert from this form into functional composition. I've tried substituing dots in various combinations, but GHCi rejects all of them....
Being an idle goodfornothing, I thought I would make a computer do the expansion for me. So into GHCi, I typed
let pu x = "(\\_ -> " ++ x ++ ")"
let f >*< a = "(\\g -> " ++ f ++ " g (" ++ a ++ " g))"
So now I have funny versions of pure and <*> which map strings which look like expressions to string which look like more complicated expressions. I then defined, similarly, the analogue of sequenceA, replacing functions by strings.
let sqa [] = pu "[]" ; sqa (f : fs) = (pu "(:)" >*< f) >*< sqa fs
I was then able to generate the expanded form of the example as follows
putStrLn $ sqa ["(+3)","(+2)"] ++ " 3"
which duly printed
(\g -> (\g -> (\_ -> (:)) g ((+3) g)) g ((\g -> (\g -> (\_ -> (:)) g ((+2) g)) g ((\_ -> []) g)) g)) 3
This last, copied to the prompt, yielded
[6,5]
Comparing the output from my "metaprogram" with the attempt in the question shows a shorter initial prefix of lambdas, arising from a shallower nesting of <*> operations. Remember, it's
(pure (:) <*> (+3)) <*> ((pure (:) <*> (+2)) <*> pure [])
so the outer (:) should be only three lambdas deep. I suspect the proposed expansion may correspond to a differently bracketed version of the above, perhaps
pure (:) <*> (+3) <*> pure (:) <*> (+2) <*> pure []
Indeed, when I evaluate
putStrLn $ pu "(:)" >*< "(+3)" >*< pu "(:)" >*< "(+2)" >*< pu "[]" ++ " 3 "
I get
(\g -> (\g -> (\g -> (\g -> (\_ -> (:)) g ((+3) g)) g ((\_ -> (:)) g)) g ((+2) g)) g ((\_ -> []) g)) 3
which looks like it matches the (updated)
(\d -> (\c -> (\b -> (\a -> (\_ -> (:)) a ((+3) a)) b ((\_ -> (:)) b)) c ((+2) c)) d ((\_ -> []) d)) 3
I hope this machine-assisted investigation helps to clarify what's going on.
You rewrote (\_ -> (:)) <*> (+3) as \a -> (\_ -> (:)) a (+3) a, which is rewriting f <*> g as f x g x instead of f x (g x). I think you made that mistake for every <*>.
It might be easier to use combinators, e.g. _S and _K, symbolically, and not their definitions as lambda-expressions,
_S f g x = f x (g x)
_K x y = x
With functions, fmap is (.) and <*> is _S, as others already mentioned. So,
sequenceA [(+3),(+2)] 3 ==
( ((:) <$> (+3)) <*> sequenceA [(+2)] ) 3 ==
_S ((:).(+3)) ( ((:) <$> (+2)) <*> pure [] ) 3 ==
_S ((:).(+3)) ( _S ((:).(+2)) (_K []) ) 3 ==
((:).(+3)) 3 ( _S ((:).(+2)) (_K []) 3 ) ==
((:).(+3)) 3 ( ((:).(+2)) 3 (_K [] 3) ) ==
(6:) ( (5:) [] ) ==
[6,5]
So it might be easier to decompose expressions down to basic functions and combinators and stop there (i.e. not decomposing them to their lambda expressions), using their "re-write rules" in manipulating the expression to find its more comprehensible form.
If you wanted to, you could now write down for yourself a more abstract, informal re-write rule for sequenceA as
sequenceA [f,g,..., z] ==
_S ((:).f) . _S ((:).g) . _S ..... . _S ((:).z) . _K []
and so
sequenceA [f,g,..., z] a ==
((:).f) a $ ((:).g) a $ ..... $ ((:).z) a $ _K [] a ==
(f a:) $ (g a:) $ ..... $ (z a:) $ [] ==
[f a, g a, ..., z a]
and hence
sequenceA fs a == map ($ a) fs == flip (map . flip ($)) fs a
to wit,
Prelude Control.Applicative> flip (map . flip ($)) [(+3),(+2)] 3
[6,5]

Why does the pointfree version of this function look like this?

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

Resources