我刚刚被告知GHC 7.8中的Coercible
,这看起来很棒.在这种情况下,我想解决我的具体问题一个同样好的问题是:有没有办法询问GHC关于哪一对类型a,b有一个Coercible a b实例(在当前平台上,比如说)?
在我看来,对于coerce :: Coercible a b => a -> b
在编译器和平台无关的程序中有用,人们需要知道 – 最好只在编译时,但也可能在编写代码时明确地说 – 是否存在给定的Coercible ab实例给定的平台,否则使用较慢的非noop后备(通过CPP,我猜).
后续问题:GHC提供功能是否有意义
coerceOrConvert :: (a -> b) -> a -> b
与coerceOrConvert f的属性
>如果当前的GHC版本和平台存在Coercible a b实例,则强制执行
> f如果没有
我意识到这对于普通的类型组来说没什么意义,但是Coercible似乎远非普通,所以我很难说…
解决方法
它保证新类型只改变类型信息而不改变底层表示,因此如果我们有(标准示例)
newtype Age = Age { unAge :: Int }
那么我们应该能够对这样的事情充满信心
instance Num Age where Age a + Age b = Age (a + b) ...
与Int上的()完全一样快 – 即.幕后没有指针间接.事实上,GHC在这里毫无困难地消除了Age构造函数.当我们想要做类似的事情时,挑战就出现了
map Age :: [Int] -> [Age]
因为Int和Age在结构上是相同的,所以我们也应该是no-op —我们必须做的就是在编译时满足类型系统,然后在运行时将map运行掉.遗憾的是,事实并非如此,因为即使在每个阶段都没有做任何事情,地图仍然会遍历我们的列表.
在许多新类型被抛出的情况下,我们也希望GHC生成最紧密的编译代码,您可能会看到(危险,小心)使用unsafeCoerce
unsafeCoerce :: [Int] -> [Age]
在这种情况下,unsafeCoerce是“安全的”,因为我们知道这两种类型在运行时是相同的.此外,由于unsafeCoerce纯粹在类型级别运行,并且在运行时是真正的无操作,我们知道,与map Age不同,unsafeCoerce确实是O(0)强制.
但这很危险.
强制希望通过允许实例化来解决这个问题
instance Coercible a b => Coercible [a] [b] where coerce = unsafeCoerce
因此,Haskell类型类机器允许仅在安全时使用强制,而不像unsafeCoerce.为了确保这种情况,不可能构建恶意的Coercible实例.为此,所有Coercible实例都是由编译器根据newtype的使用构建的.
最后要说明的是,当你真正深入了解Coercible如何工作时,你必须要了解新的Haskell角色系统,该系统允许开发人员注释新类型是否应该允许强制. [Coercible类的文档](http://www.haskell.org/ghc/docs/7.8.1-rc2/html/libraries/base-4.7.0.0/Data-Coerce.html)清楚地概述了这一点.