未装箱的类型,如Int#
和严格的功能,如f (!x) = ...
,是不同的,但我看到概念上的相似性 - 他们在某种程度上不允许暴力/懒惰。如果Haskell是像Ocaml这样的严格语言,那么每个函数都是严格的,并且每个类型都是未装箱的。未装箱的类型与强制执行之间的关系是什么?
答案 0 :(得分:35)
未装箱与装箱数据
为了支持parametric polymorphism和laziness,默认情况下,Haskell数据类型统一表示为closure上the heap的指针,其结构如下:
这些是“盒装”值。 unboxed 对象由值本身直接表示,没有任何间接或闭包。 <{1}}已装箱,但Int
已取消装箱。
延迟值需要一个盒装表示。严格的值不会:它们既可以表示为堆上的完全计算闭包,也可以表示为原始的未装箱结构。请注意,pointer tagging是我们可以在盒装对象上使用的优化,用于在指向闭包的指针中编码构造函数。
与严格的关系
通常,未装箱的值是由功能语言编译器以临时方式生成的。然而,在Haskell中,unboxed values是特殊的。它们:
Int#
; 因为他们没有举起,所以他们必须严格。懒惰的表现是不可能的。
因此,特定的未装箱类型,如#
,Int#
,在机器上实际上只表示为double或int(用C表示法)。
严格性分析
另外,GHC执行strictness analysis常规Haskell类型。如果发现值的使用是严格的 - 即它永远不会'未定义' - 优化器可能会将常规类型的所有用法(例如Double#
)替换为未装箱的(Int
),因为它知道Int#
的使用始终是严格的,因此用更有效(并且始终严格)的Int
类型替换是安全的。
我们当然可以有没有未装箱类型的严格类型,例如,元素严格的多态列表:
Int#
在其元素中是严格的,但不会将它们表示为未装箱的值。
这也指出了你对严格语言like OCaml的错误。他们仍然需要支持多态,因此要么提供统一的表示,要么将数据类型和函数专门化为每种类型。 GHC默认使用统一表示,OCaml也是如此,尽管GHC现在也可以specialize types and functions(就像C ++模板一样)。
答案 1 :(得分:16)
未装箱的类型必须严格,但并非所有严格的值都必须取消装箱。
data Foo a = Foo !a !a
有两个严格的字段
data Bar a = Bar {-# UNPACK #-} !Int !a
有两个严格的字段,但第一个字段是未装箱的。
最终,未装箱的类型(必然)严格的原因是没有地方存储thunk,因为它们只是平坦,愚蠢的数据。
答案 2 :(得分:8)
任何类型的参数都可以“严格”,但唯一具有相应盒装类型的未装箱类型是Char#
,Int#
,Word#
,Double#
和{ {1}}。
如果您了解像C这样的低级语言,则更容易解释。未装箱的类型就像Float#
,int
等,盒装类型就像double
,int*
等。当你有double*
时,你已经知道它在位模式中表示的整个值,因此,它不是懒惰的。它也必须严格,因为int
的所有值都是有效的而不是⊥。
但是,给定int
,您可以选择稍后取消引用指针以获取实际值(因此是惰性的),并且可能有无效指针(它包含⊥,即非严格)。 / p>