我有一个带有幻像类型的 newtype
,但我在轻松使用它时遇到了困难。
考虑以下示例:
import Data.Coerce
newtype F a b = F b
myzip :: (Num b) => F a b -> F a b
myzip = (coerce (+)) myf
myf :: F a b
myf = undefined
GHC 提示它无法将类型“a0”的表示与“Int”的表示相匹配
。基本上,coerce (+)
的类型是 F a0 -> F a -> F a
,而不是我想要的类型,即 F a -> F a -> F a
.
考虑到a
当前的phantom
角色,这是合理的。我想知道是否有一种简单的方法可以通过其他方法进行类型推断(我不想写出签名,这就是我首先使用 coerce
的原因!) 。
特别是,我期望如果我将参数 a
的作用限制为名义值,那么 GHC 将能够知道签名的唯一可能选择就是我想要的。没有这样的运气:GHC 给出了同样的错误。
如果重要的话,在我的实际代码中,a
的角色必须至少是代表性的,而 b
必须是名义性的。
最佳答案
让我们首先看看几种类型:
coerce (+) :: (Num n, Coercible (n -> n -> n) b) => b
myf :: F c d
第一个问题是 GHC 无法确定 n
,因此它可以选择 (+)
的实现。
让我们看看如果我们使用作用域类型变量来尝试一个简单的解决方案会发生什么:
myzip :: forall a b . Num b => F a b -> F a b
myzip = coerce ((+) :: b -> b -> b) myf
这不被接受。为什么?因为myf
的类型是不明确的!如果我们用另一个参数扩展 myzip
,我们可以更清楚地看到这一点:
myzip x = coerce ((+) :: b -> b -> b) myf x
根据 Coercible
与类型构造函数(在本例中为 ->
)的配合方式,我们可以将所有内容排列起来并得出 myf< 的类型
必须可与 b
强制转换,其中 x::F a b
。但在此上下文中 myf
的类型是不明确的。因此,GHC 无法在 b
和 myf
类型之间选择适当的 Coercible
实例。因为coerce
实际上是底层的恒等函数,所以选择哪个并不重要,但 GHC 根本不会根据不明确的类型变量选择实例。为了使这项工作成功,你需要类似的东西
myzip :: forall a b . Num b => F a b -> F a b
myzip = (coerce ((+) :: b -> b -> b)) (myf :: F a b)
输入角色
类型角色不能(至少目前)以任何方式指导实例解析。他们所能做的就是确定生成了哪些Coercible
实例。鉴于
class c => X d
GHC 将得出结论:X d
必然包含 c
。但考虑到
instance c => X d
GHC 将不会得出X d
必然包含c
的结论。我不确定具体原因,但情况一直如此。
关于haskell - 使用名义角色进行类型推断,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32596138/