通常,Haskell 中处理的强制类型有两种:表示相等(通过newtype
和Coercible
)和关于类型变量的新信息(通过Typeable
)。第二种与运行时表示关系不大,所以我只描述Coercible
/newtype
机制。
这是保证newtype
只改变类型信息而不是底层表示,因此如果我们有(标准示例)
newtype Age = Age { unAge :: Int }
那么我们应该能够对类似的东西充满信心
instance Num Age where
Age a + Age b = Age (a + b)
...
(+)
和on一样快Int
——即幕后没有指针间接进行。事实上,GHC 在Age
这里毫不费力地消除了构造函数。当我们想做类似的事情时,挑战就来了
map Age :: [Int] -> [Age]
因为Int
和在结构上是相同的,所以这也应该是一个无操作——我们必须做的就是在编译时满足类型系统,然后在运行时在操作上Age
丢弃。map Age
可悲的是,情况并非如此,因为map
即使它在每个阶段什么都不做,它仍然会遍历我们的列表。
在大量newtype
s 被抛出但我们也希望 GHC 生成您可能会看到(危险的、小心的)使用的最紧凑的编译代码的情况下unsafeCoerce
unsafeCoerce :: [Int] -> [Age]
在这种情况下unsafeCoerce
是“安全的”,因为我们知道这两种类型在运行时是相同的。此外,由于unsafeCoerce
纯粹在类型级别上操作并且在运行时是真正的无操作,我们知道 like map Age
,unsafeCoerce
确实是一种O(0)
强制。
但这非常危险。
Coercible
希望通过允许像这样的实例化来解决这个问题
instance Coercible a b => Coercible [a] [b] where coerce = unsafeCoerce
因此 Haskell 类型类机器coerce
只允许在安全时使用,不像unsafeCoerce
. 为了确保这种情况,必须不可能构建恶意实例Coercible
。为此,所有Coercible
实例都是由编译器基于newtype
.
最后一点,当您真正深入了解Coercible
其工作原理时,您必须了解新的 Haskell 角色系统,该系统允许开发人员注释 a 是否newtype
应该允许强制。这在 [Coercible
类文档] ( http://www.haskell.org/ghc/docs/7.8.1-rc2/html/libraries/base-4.7.0.0/Data-Coerce.html ) 中有明确的概述。