存在rseq / seq是否会破坏参照透明度?是否有一些替代方法没有?

时间:2012-12-02 19:48:31

标签: haskell ghc compiler-optimization strictness

我一直认为用x :: ()替换表达式() :: ()将是编译Haskell程序时最基本的优化之一。由于()只有一个居民,无论x是什么,结果都是()。在我看来,这种优化是参考透明度的重要结果。我们可以为只有一个居民的任何类型做这样的优化。

更新:我在这个问题上的推理来自于自然演绎规则。单位类型对应于真值(⊤),我们有一个扩展规则“if x : ⊤ then {{ 1}}“。例如,参见this text第20页。我认为用扩展或契约替换表达式是安全的。)

此优化的一个结果是() : ⊤将被undefined :: ()取代,但我不认为这是一个问题 - 它只会使程序变得更加懒惰(并且依赖于{ {1}}当然是一种糟糕的编程习惯。)

然而今天我意识到这样的优化会完全打破() :: ()undefined :: ()定义为

Control.Seq

我们有

Strategy

但是type Strategy a = a -> () 因此优化只会放弃对{WNF -- | 'rseq' evaluates its argument to weak head normal form. rseq :: Strategy a rseq x = x `seq` () 所需的评估。

问题出在哪里?

  • rseq x :: ()x的存在是否会破坏参照透明度(即使我们只考虑终止表达式)?
  • 或者这是rseq设计的缺陷,我们可以设计一种更好的方法来强制WHNF表达式与这种优化兼容吗?

4 个答案:

答案 0 :(得分:11)

参照透明度是关于等式语句和变量引用。如果你说 x = y 并且你的语言是引用透明的,那么你可以用 y (模数范围)替换每个出现的 x 。< / p>

如果您尚未指定x = (),则无法安全地将x替换为(),例如您的情况。这是因为你对()的居民错了,因为在Haskell中有两个:一个是()的唯一构造函数,即()。另一个是从未计算过的值。您可以将其命名为 bottom undefined

x :: ()
x = x

你当然不能在x替换任何(),因为那会有机会语义。语言语义中 bottom 的存在允许一些尴尬的边缘情况,特别是当你有一个seq组合子时,你甚至可以证明每个monad错误。这就是为什么在许多正式讨论中我们忽略 bottom 的存在。

然而,参考透明度不会受此影响。 Haskell仍然是引用透明的,因此是一种纯函数式语言。

答案 1 :(得分:7)

您对引用透明度的定义不正确。引用透明度并不意味着您可以将x :: ()替换为() :: (),并且一切都保持不变;这意味着你可以用它的定义替换所有出现的变量,并且一切都保持不变。如果使用此定义,seqrseq与引用透明度不冲突。

答案 2 :(得分:4)

seq在这里是一个红鲱鱼。

unitseq :: () -> a -> a
unitseq x y = case x of () -> y

这与单位上的seq具有相同的语义,并且不需要任何魔法。事实上,seq在处理无法模式匹配的事物时只有一些“魔力” - 即功能。

使用undefined :: ()替换()与使用unitseq时的seq具有相同的不良影响。

一般来说,我们认为价值观不仅仅是“它们”,而是如何定义。从这个角度来看,应该清楚为什么我们不能不顾一切地实施改变定义的转变。

答案 3 :(得分:2)

感谢大家的鼓舞人心的答案。在考虑了更多之后,我得出以下观点:

视图1:仅考虑终止表达式

如果我们将自己局限于规范化系统,那么seq(或rseq等)对程序的结果没有影响。它可以改变程序使用的内存量并增加CPU时间(通过评估我们实际上不需要的表达式),但结果是一样的。因此,在这种情况下,规则x :: () --> () :: ()是可以接受的 - 不会丢失任何内容,可以节省CPU时间和内存。

观点2:Curry-Howard同构

由于Haskell是Turing-complete,因此任何类型都存在于无限循环中,因此C-H对应的逻辑系统是不一致的(正如Vitus所指出的那样) - 任何事物都可以被证明。所以实际上任何规则都可以在这样的系统中使用。这可能是我最初想法中最大的问题。

视图3:扩展规则

自然演绎规则为不同类型的运营商提供减少和扩展。例如,对于->,它是β减少和η-展开,对于(,)它是

  

fst (x, y) --reduce - &gt; x(同样适用于snd

     

x : (a,b) --expand - &gt; (fst x, snd x)

等。由于存在底部,减少规则仍然是可以接受的(我相信),但扩展规则不是!特别是->

rseq (undefined :: Int -> Int) = undefined

但它是η-扩张

rseq (\x -> (undefined :: Int -> Int) x) = ()

(,)

case undefined of (a,b) -> ()

case (fst undefined, snd undefined) of (a,b) -> ()

等。因此,以同样的方式,()的扩展规则不可接受。

视图4:延迟模式匹配

允许x :: ()扩展为() :: ()可以被视为强制单构造函数数据类型上的所有模式匹配为lazy的特殊情况。我从未见过在多构造函数数据类型上使用的延迟模式匹配,所以我相信这个改变将允许我们完全摆脱~惰性模式匹配符号。但是,它会使语言的语义变得更加懒惰(另请参阅Daniel Fischer对该问题的评论)。而且(如问题所述)中断Strategy