I was reading the paper authored by Simon Peyton Jones, et al. named “Playing by the Rules: Rewriting as a practical optimization technique in GHC”. In the second section, namely “The basic idea” they write:
Consider the familiar
map
function, that applies a function to each element of a list. Written in Haskell,map
looks like this:
map f [] = []
map f (x:xs) = f x : map f xs
Now suppose that the compiler encounters the following call of
map
:
map f (map g xs)
We know that this expression is equivalent to
map (f . g) xs
(where “.” is function composition), and we know that the latter expression is more efficient than the former because there is no intermediate list. But the compiler has no such knowledge.
One possible rejoinder is that the compiler should be smarter --- but the programmer will always know things that the compiler cannot figure out. Another suggestion is this: allow the programmer to communicate such knowledge directly to the compiler. That is the direction we explore here.
My question is, why can't we make the compiler smarter? The authors say that “but the programmer will always know things that the compiler cannot figure out”. However, that's not a valid answer because the compiler can indeed figure out that map f (map g xs)
is equivalent to map (f . g) xs
, and here is how:
map f (map g xs)
map g xs
unifies withmap f [] = []
.Hence
map g [] = []
.map f (map g []) = map f []
.map f []
unifies withmap f [] = []
.Hence
map f (map g []) = []
.map g xs
unifies withmap f (x:xs) = f x : map f xs
.Hence
map g (x:xs) = g x : map g xs
.map f (map g (x:xs)) = map f (g x : map g xs)
.map f (g x : map g xs)
unifies withmap f (x:xs) = f x : map f xs
.Hence
map f (map g (x:xs)) = f (g x) : map f (map g xs)
.
Hence we now have the rules:
map f (map g []) = []
map f (map g (x:xs)) = f (g x) : map f (map g xs)
As you can see f (g x)
is just (f . g)
and map f (map g xs)
is being called recursively. This is exactly the definition of map (f . g) xs
. The algorithm for this automatic conversion seems to be pretty simple. So why not implement this instead of rewriting rules?