data Bool = False | True
自GHC-8.2.1以来,-XUnboxedSums扩展允许以更高内存效率的方式定义和类型.以下是文档引用:
In the degenerate case where all the alternatives have zero width, such as the
Bool
-like(# (# #) | (# #) #)
, the unboxed sum layout only has an Int32 tag field (i.e., the whole thing is represented by an integer).
我想知道,这种表示是否比使用简单的普通Haskell枚举数据类型更有效?
以下是完整的文档:
> https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/glasgow_exts.html#unboxed-sums
语义和表征这两种类型不能互换使用.
类型
data Bool = False | True
是一种提升型.这意味着变量x :: Bool仍然可以不被评估(例如thunk).因此它将表示为指针.
相反
(# (# #) | (# #) #)
是未提升的,实际上只能有两个值,并且只表示为机器整数.
空间效率
但这并不意味着后者更节省空间:因为True和False是nullary构造函数(它们不带参数),它们在程序的静态代码中一劳永逸地存在,并且指针只指向它们.因此,在所有情况下,成本都是一个机器字.
运行时效率
未装箱的变体可能比Bool稍微有效:
>对于Bool,代码首先必须检查是否已经评估过?然后它可以分支在哪个构造函数上.
分支非常有效:代码不必遵循指针:指针地址的低位中的“指针标记”将指示它是哪个构造函数.但是,屏蔽其他位的成本很低.
>在未装箱的变体中,它只是0或1,并且不需要进行thunk检查.
在数据类型中取消装箱
如果您定义这样的数据类型
data Foo = Foo (-# UNBOX #-} !Bool
你应该得到与你写的完全相同的结果
data Foo = Foo (# (# #) | (# #) #)
因为这是未装箱的和扩展的主要目的.
在功能中取消装箱
如果在Bool参数中定义一个严格的函数,例如
foo True = "Hello" foo False = "World!"
然后我可以想象(但没有检查)编译器将其优化为一个带有(#(##)|(##)#)的工作器和一个负责thunk-check的包装器.然后包装器可能在使用foo的use-sites中内联,并且所有内容都可能以(#(##)|(##)#)结束.
结论
不要打扰.