【发布时间】:2012-11-20 07:39:52
【问题描述】:
我一直认为用() :: () 替换表达式x :: () 将是编译Haskell 程序期间最基本的优化之一。由于() 有一个居民,所以无论x 是什么,它的结果都是()。在我看来,这种优化是参照透明性的重要结果。我们可以对任何类型的人进行这样的优化,只需要一个居民。
(更新:我在这件事上的推理来自自然演绎规则。单元类型对应于真(⊤),我们有一个扩展规则“如果x : ⊤然后() : ⊤” . 例如,参见this text 第 20 页。我认为用扩展或收缩替换表达式是安全的。)
这种优化的一个结果是undefined :: () 将被() :: () 取代,但我不认为这是一个问题——它只会让程序变得更懒惰(并且依赖undefined :: () 肯定是一种糟糕的编程习惯)。
但是今天我意识到这样的优化会完全破坏Control.Seq。 Strategy 定义为
type Strategy a = a -> ()
我们有
-- | 'rseq' evaluates its argument to weak head normal form.
rseq :: Strategy a
rseq x = x `seq` ()
但是rseq x :: () 所以优化会简单地将x 所需的评估丢弃到WHNF。
那么问题出在哪里?
-
rseq和seq的存在是否会破坏引用透明度(即使我们只考虑终止表达式)? - 或者这是
Strategy的设计缺陷,我们可以设计一种更好的方法来强制表达式与此类优化兼容的 WHNF?
【问题讨论】:
-
x可能不会终止,然后用()替换它可以改变程序的含义。main = case last (repeat ()) of { () -> launchMissiles }. -
@DanielFischer 我同意这一点,优化会改变程序的含义。但这只是一个人为的例子——你能给出一个有意义的、真实的程序,它的意义会改变吗?没有人(可能除了
Strategy)依赖_|_ :: (),我怀疑这样做是否合理。即使我们只考虑终止表达式,rseq+ 优化也会出现问题。 -
如果
x终止,将rseq x替换为()不会改变程序的含义。当且仅当x不终止时,该替换才会改变含义。不过,我无法给出一个有意义的示例程序,该程序依赖于()直接被 ⊥ 占据。但神奇的seq的关键在于没有进行这样的替换。seq与该语言的其余部分有些不一致,但是将空间和时间泄漏转化为工作程序而没有它太有用了。这就是为什么尽管它具有魔力,但它仍然存在于该语言中。 -
Petr:我不知道你说
_|_ :: ()不是一个有意义的、真实的程序是什么意思。 (我猜你并不是说它缺乏意义/语义并且它不存在。) -
@Conal 不,我不是这个意思。我的意思是我希望看到一个真正的程序或库(当然除了使用
Strategy或seq的程序或库),如果我们允许x :: () --> () :: ()规则,其含义将会改变。或者,在更广泛的背景下陈述:如果我们将单构造函数数据类型上的所有模式匹配自动变得惰性,我们会失去什么? 这种语义变化会导致某些程序更难编写吗? (此更改将暗示优化规则 - 就像 Daniel 的示例将变为case last (repeat ()) of { ~() -> launchMissiles },它总是会发射导弹。)
标签: haskell ghc compiler-optimization strictness