对于带有约束的表达式,Haskell中的求值是如何工作的

对于带有约束的表达式,Haskell中的求值是如何工作的,haskell,polymorphism,evaluation,strictness,Haskell,Polymorphism,Evaluation,Strictness,假设我用GHCi写: GHCi> let x = 1 + 2 :: Integer GHCi> seq x () GHCi> :sprint x GHCi按预期打印x=3 但是, GHCi> let x = 1 + 2 GHCi> seq x () GHCi> :sprint x 收益率x=\uu 这两个表达式之间唯一的区别是它们的类型(Integervsnuma=>a)。我的问题是到底发生了什么,以及为什么在后一个例子中似乎没有计算x。主要问题是 let

假设我用GHCi写:

GHCi> let x = 1 + 2 :: Integer
GHCi> seq x ()
GHCi> :sprint x
GHCi按预期打印
x=3

但是,

GHCi> let x = 1 + 2
GHCi> seq x ()
GHCi> :sprint x
收益率
x=\uu

这两个表达式之间唯一的区别是它们的类型(
Integer
vs
numa=>a
)。我的问题是到底发生了什么,以及为什么在后一个例子中似乎没有计算
x

主要问题是

let x = 1 + 2
为所有a定义类型为
的多态值。Num a=>a
,其计算结果与函数类似

每次使用
x
都可以使用不同的类型,例如
x::Int
x::Integer
x::Double
等等。这些结果不会以任何方式“缓存”,而是每次重新计算,就好像
x
是一个被多次调用的函数

实际上,类型类的常见实现将这种多态的
x
作为一个函数来实现

x :: NumDict a -> a
其中上面的
NumDict a
参数由编译器自动添加,并携带有关
a
Num
类型的信息,包括如何执行加法,如何解释
a
中的整数文本,等等。这称为“字典传递”实现

因此,多次使用多态
x
确实相当于多次调用函数,导致重新计算。为了避免这种情况,Haskell引入了(可怕的)单态限制,迫使
x
成为单态。MR不是一个完美的解决方案,在某些情况下可能会产生一些令人惊讶的类型错误


为了缓解这个问题,在GHCi中默认禁用MR,因为在GHCi中,我们不太关心性能——可用性在那里更重要。但是,正如您所发现的那样,这会导致重新计算。

我认为值得一提的是正在进行的字典传递。