我一直认为用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表达式与这种优化兼容吗?答案 0 :(得分:11)
参照透明度是关于等式语句和变量引用。如果你说 x = y 并且你的语言是引用透明的,那么你可以用 y (模数范围)替换每个出现的 x 。< / p>
如果您尚未指定x = ()
,则无法安全地将x
替换为()
,例如您的情况。这是因为你对()
的居民错了,因为在Haskell中有两个:一个是()
的唯一构造函数,即()
。另一个是从未计算过的值。您可以将其命名为 bottom 或 undefined :
x :: ()
x = x
你当然不能在x
替换任何()
,因为那会有机会语义。语言语义中 bottom 的存在允许一些尴尬的边缘情况,特别是当你有一个seq
组合子时,你甚至可以证明每个monad错误。这就是为什么在许多正式讨论中我们忽略 bottom 的存在。
然而,参考透明度不会受此影响。 Haskell仍然是引用透明的,因此是一种纯函数式语言。
答案 1 :(得分:7)
您对引用透明度的定义不正确。引用透明度并不意味着您可以将x :: ()
替换为() :: ()
,并且一切都保持不变;这意味着你可以用它的定义替换所有出现的变量,并且一切都保持不变。如果使用此定义,seq
和rseq
与引用透明度不冲突。
答案 2 :(得分:4)
seq
在这里是一个红鲱鱼。
unitseq :: () -> a -> a
unitseq x y = case x of () -> y
这与单位上的seq
具有相同的语义,并且不需要任何魔法。事实上,seq
在处理无法模式匹配的事物时只有一些“魔力” - 即功能。
使用undefined :: ()
替换()
与使用unitseq
时的seq
具有相同的不良影响。
一般来说,我们认为价值观不仅仅是“它们”,而是如何定义。从这个角度来看,应该清楚为什么我们不能不顾一切地实施改变定义的转变。
答案 3 :(得分:2)
感谢大家的鼓舞人心的答案。在考虑了更多之后,我得出以下观点:
如果我们将自己局限于规范化系统,那么seq
(或rseq
等)对程序的结果没有影响。它可以改变程序使用的内存量并增加CPU时间(通过评估我们实际上不需要的表达式),但结果是一样的。因此,在这种情况下,规则x :: () --> () :: ()
是可以接受的 - 不会丢失任何内容,可以节省CPU时间和内存。
由于Haskell是Turing-complete,因此任何类型都存在于无限循环中,因此C-H对应的逻辑系统是不一致的(正如Vitus所指出的那样) - 任何事物都可以被证明。所以实际上任何规则都可以在这样的系统中使用。这可能是我最初想法中最大的问题。
自然演绎规则为不同类型的运营商提供减少和扩展。例如,对于->
,它是β减少和η-展开,对于(,)
它是
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) -> ()
等。因此,以同样的方式,()
的扩展规则不可接受。
允许x :: ()
扩展为() :: ()
可以被视为强制单构造函数数据类型上的所有模式匹配为lazy的特殊情况。我从未见过在多构造函数数据类型上使用的延迟模式匹配,所以我相信这个改变将允许我们完全摆脱~
惰性模式匹配符号。但是,它会使语言的语义变得更加懒惰(另请参阅Daniel Fischer对该问题的评论)。而且(如问题所述)中断Strategy
。