未装箱的类型,likeInt#
和严格的函数,likef (!x) = ...
是不同的,但我看到概念上的相似性——它们在某种程度上不允许 thunk/laziness。如果 Haskell 是像 Ocaml 这样的严格语言,那么每个函数都将是严格的,并且每个类型都是未装箱的。未装箱类型与执行严格性之间有什么关系?
3 回答
未装箱与装箱数据
为了支持参数多态性和惰性,默认情况下,Haskell 数据类型统一表示为指向堆上的闭包的指针,其结构如下:
(来源:haskell.org)
这些是“盒装”值。未装箱的对象直接由值本身表示,没有任何间接或闭包。Int
已装箱,但未Int#
装箱。
惰性值需要盒装表示。严格值不会:它们可以表示为堆上完全评估的闭包,也可以表示为原始的未装箱结构。请注意,指针标记是我们可以在装箱对象上使用的一种优化,用于在指向闭包的指针中编码构造函数。
与严格的关系
通常,未装箱的值由函数式语言编译器以特别的方式生成。然而,在 Haskell 中,未装箱的值是特殊的。他们:
- 他们有不同的种类,
#
; - 只能在特殊场所使用;和
- 它们没有被提升,因此不表示为指向堆值的指针。
因为它们没有被提升,所以它们必然是严格的。懒惰的表现是不可能的。
因此,特定的未装箱类型,例如Int#
, Double#
,实际上在机器上被表示为 double 或 int (在 C 表示法中)。
严格性分析
另外,GHC对常规 Haskell 类型进行严格分析。如果发现一个值的使用是严格的——即它永远不会是“未定义的”——优化器可能会将所有常规类型的使用(例如Int
)替换为未装箱的(Int#
),因为它知道使用Int
总是严格的,因此用更有效(并且总是严格)的类型替换Int#
是安全的。
我们当然可以有没有未装箱类型的严格类型,例如,一个元素严格的多态列表:
data List a = Empty | Cons !a (List a)
其元素是严格的,但不将它们表示为未装箱的值。
这也指出了您对严格语言(如 OCaml )所犯的错误。它们仍然需要支持多态性,因此它们要么提供统一的表示,要么将数据类型和函数专门用于每种类型。GHC 默认使用统一表示,OCaml 也是如此,尽管 GHC 现在也可以专门化类型和函数(如 C++ 模板)。
未装箱的类型必须是严格的,但并非所有严格的值都必须是未装箱的。
data Foo a = Foo !a !a
有两个严格的领域
data Bar a = Bar {-# UNPACK #-} !Int !a
有两个严格的字段,但第一个是未装箱的。
归根结底,未装箱类型(必然)严格的原因是没有地方存储 thunk,因为它们在那时只是扁平的、愚蠢的数据。
任何类型的参数都可以设置为“严格”,但唯一具有相应装箱类型的未装箱类型是Char#
、Int#
、Word#
和。Double#
Float#
如果您了解 C 等低级语言,则更容易解释。未装箱的类型如int
,double
等,装箱的类型如int*
,double*
等。当您拥有 时int
,您已经知道整个值,因为它在位模式中表示,因此,它不是惰性的。它也必须是严格的,因为 的所有值int
都是有效的,而不是 ⊥。
但是,给定一个,int*
您可以选择稍后取消引用指针以获取实际值(因此是惰性的),并且可能有无效的指针(它包含⊥,即非严格)。