Search code examples
haskellpointfree

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?


Solution

  • 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].