我正在阅读 Simon Peyton Jones 等人撰写的论文。命名为“按规则行事:重写作为 GHC 中一种实用的优化技术”。在第二部分,即“基本思想”中,他们写道:
考虑熟悉的
map
函数,它将函数应用于列表的每个元素。用 Haskell 编写,map
如下所示:
map f [] = []
map f (x:xs) = f x : map f xs
现在假设编译器遇到以下调用
map
:
map f (map g xs)
我们知道这个表达式等价于
map (f . g) xs
(其中“.”是函数组合),我们知道后一种表达式比前一种更有效,因为没有中间列表。但是编译器没有这样的知识。
一种可能的反驳是编译器应该更聪明——但程序员总是知道编译器无法弄清楚的事情。另一个建议是:允许程序员将这些知识直接传达给编译器。这就是我们在这里探索的方向。
我的问题是,为什么我们不能让编译器更智能?作者说“但程序员总是知道编译器无法弄清楚的事情”。但是,这不是一个有效的答案,因为编译器确实可以计算出它map f (map g xs)
等价于map (f . g) xs
,这是如何:
map f (map g xs)
map g xs
与 统一map f [] = []
。因此
map g [] = []
。map f (map g []) = map f []
.map f []
与 统一map f [] = []
。因此
map f (map g []) = []
。map g xs
与 统一map f (x:xs) = f x : map f xs
。因此
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)
与 统一map f (x:xs) = f x : map f xs
。因此
map f (map g (x:xs)) = f (g x) : map f (map g xs)
。
因此,我们现在有规则:
map f (map g []) = []
map f (map g (x:xs)) = f (g x) : map f (map g xs)
如您所见f (g x)
, is just(f . g)
并且map f (map g xs)
正在被递归调用。这正是 的定义map (f . g) xs
。这种自动转换的算法似乎很简单。那么为什么不实现这个而不是重写规则呢?