Haskell类型是否可以通过强制转换与它们的C语言对应类型相同?

8

我该如何确定在给定平台上Haskell类型是否具有等效的Coercible实例?

我刚刚听说了GHC 7.8中的Coercible,这似乎很棒。在这种情况下,我想解决我的具体问题同样好的一个问题是:是否有一种方法可以询问GHC关于哪些类型对ab存在Coercible a b实例(在当前平台上)?

我认为,对于coerce :: Coercible a b => a -> b在编译器和平台无关的程序中发挥作用,人们需要知道是否存在给定平台上的Coercible a b实例,最好是在编译时知道,但也可能需要在编写代码时明确指出,并使用较慢的非空操作回退(通过CPP,我猜测)。
后续问题:GHC提供一个函数是否有意义?
coerceOrConvert :: (a -> b) -> a -> b

具有以下属性:coerceOrConvert f

  • 如果当前GHC版本和平台存在Coercible a b实例,则为coerce

  • 否则为f

我意识到这对于普通类型类来说几乎没有意义,但是Coercible似乎远非普通,所以很难说......

1个回答

5
通常在 Haskell 中处理的强制类型转换有两种方式:通过 newtype 和 Coercible 实现的表示相等,以及通过 Typeable 实现的有关类型变量的新信息。第二种类型与运行时表示几乎没有关系,因此我将只描述 Coercible/newtype 机制。
这是一个保证 newtype 仅更改类型信息而不更改底层表示的机制,因此如果我们有(标准示例):
newtype Age = Age { unAge :: Int }

那么我们应该有信心,像这样的事情

instance Num Age where
  Age a + Age b = Age (a + b)
  ...

Int 上,(+)Age 的速度是完全一样的,即在幕后没有指针间接操作。实际上,GHC 在这里毫不费力地消除了 Age 构造器。然而,当我们想要执行类似下面的操作时,就会面临挑战:

map Age :: [Int] -> [Age]

自从IntAge在结构上完全相同,这也应该是一个无操作,我们所要做的就是在编译时满足类型系统,然后在运行时丢弃map Age。不幸的是,这并不是情况,因为即使在每个阶段什么也不做,map仍将遍历我们的列表。

在许多newtype被抛出但我们也希望GHC生成最紧凑的编译代码的情况下,您可能会看到(危险,小心)使用unsafeCoerce
unsafeCoerce :: [Int] -> [Age]

在这种情况下,unsafeCoerce是“安全”的,因为我们知道这两种类型在运行时是相同的。此外,由于unsafeCoerce纯粹在类型级别上操作,并且在运行时是真正的无操作,所以与map Age不同,unsafeCoerce 真的是一个O(0)强制转换。

但它非常危险。

Coercible希望通过允许实例化来解决这个问题,例如:

instance Coercible a b => Coercible [a] [b] where coerce = unsafeCoerce

因此,Haskell类型类机制允许仅在安全情况下使用coerce,而不像unsafeCoerce那样。为确保这一点,不能构建恶意的Coercible实例。为此,所有Coercible实例都是由编译器基于对newtype的使用构建的。
最后需要注意的是,当你真正深入了解Coercible的工作原理时,你必须理解新的Haskell角色系统,该系统允许开发人员注释是否应允许强制转换newtype。这在[Coercible类的文档]中明确概述(http://www.haskell.org/ghc/docs/7.8.1-rc2/html/libraries/base-4.7.0.0/Data-Coerce.html)。

谢谢。这非常有用,但仍然有一个问题:是否有(可能是平台相关的)列表,或者(更好的是)从 GHC 生成这样的列表的方法,其中标准类型 ab 存在 Coercible a b 实例?(通过“标准类型”,我可能指的是“Haskell报告中指定的类型”或其某个子集)。 - gspr
我来举一个更具体的场景:修复具体类型 ab。假设我想编写一个转换函数 f :: a -> b。我怀疑在许多平台上,使用 GHC 会有一个 Coercible a b 实例,但并非所有平台都有(这可能发生,对吧?)。假设还有一个通用的、慢速的转换函数 g :: a -> b,它总是在不假设底层表示的情况下执行正确的操作(假设 g 在 Haskell 报告中)。我能否以某种方式(如果必须使用 CPP)说“只要有 Coercible a b 实例,就让 f = coerce,否则让 f = g”? - gspr
我认为这是不可能的。Haskell报告保证了newtype具有相同的表示形式,但并没有说太多关于应该如何实现的事情。您可以执行特定于GHC的Haskell实现的操作 - 我认为如果您深入研究reflection包的内部,您会经常看到这些内容 - 但这些内容往往是非常不安全的。Coercible的设计独立于任何实际的实现细节 - 它完全依靠newtype的承诺来工作。 - J. Abrahamson
我确实在询问GHC的具体实现,我知道Coercible与实现细节无关... 我想我在问的是:是否有一个易于访问的列表,保证它不会在次要版本中更改,其中列出了使用newtype在GHC中实现的(标准)类型?或者,可能可以在特定平台上向特定版本的GHC询问,“嘿,你是否只是从标准Haskell类型到标准Haskell类型简单地使用newtype?”,也许通过询问Coercible实例来实现?我觉得这会很有帮助。 - gspr
例如,我想说:“我们是否正在运行 GHC?”如果没有,请进行缓慢的转换。如果是,请问 GHC:“在此特定平台上,此特定版本是否定义了newtype CDouble = CDouble Double(或者是否有Coercible CDouble Double实例)?”如果没有,请进行缓慢的转换。如果是,请使用coerce进行转换。这对于涉及 FFI 的许多数值计算非常有用。 - gspr
1
你可以遵循“coerce”只在“newtype”构造函数在作用域内时才起作用的限制来指导。这意味着任何公开声明为“newtype”等效的类型都将显示在文档中。在“Foreign.C.Types”中声明的许多FFI类型正好表示为“newtypes”。如果您想要访问显式的底层内存,则需要探索“GHC.Exts”中的未装箱类型,但请注意,这些明确不是“newtypes”,尽管GHC可能会在编译期间决定取消装箱类型。 - J. Abrahamson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接