我必须连接两个库,其中元数据在一个中表示为类型参数,在另一个中表示为记录字段。我用GADT编写了一个适配器。这是一个提炼版本:
{-# LANGUAGE GADTs #-}
newtype TFId a = MkId a
data TFDup a = MkDup !a !a
data GADT tf where
ConstructorId :: GADT TFId
ConstructorDup :: GADT TFDup
main = do
f ConstructorId
f ConstructorDup
f :: GADT tf -> IO ()
f = _
这很有效。 (可能不完美;评论欢迎,但这不是问题。)
我花了一些时间才能进入这个工作状态。我最初的直觉是为TFId
使用类型系列,计算:“GADT
有种类(* -> *) -> *
;在ConstructorDup
TFDup
中有* -> *
种;因此对于ConstructorId
我可以使用以下* -> *
类型系列:“
{-# LANGUAGE TypeFamilies #-}
type family TFId a where TFId a = a
类型构造函数确实具有相同的类型* -> *
,但GHC显然不会将它放在同一个地方:
错误:......
- 类型系列'TFId'应该有1个参数,但是没有给出
- 在数据构造函数'ConstructorId'的定义中 在'GADT'
的数据类型声明中
好吧,如果这样说......
我不确定我理解为什么会产生这样的差异。没有使用类型系列没有应用它们?这是怎么回事?还有其他(更好)的方法吗?
答案 0 :(得分:3)
射性。
type family F :: * -> *
type instance F Int = Bool
type instance F Char = Bool
此处F Int ~ F Char
。然而,
data G (a :: *) = ...
永远不会导致G Int ~ G Char
。这些保证是不同的类型。
在通用量化中,如
foo :: forall f a. f a -> a
f
被允许为G
(单射)但不允许为F
(非单射)。
这是推理工作。可以推断foo (... :: G Int)
具有类型Int
。 foo (... :: F Int)
相当于foo (... :: Bool)
,可能有Int
类型,或类型Char
- 它是一个含糊不清的类型。
另请考虑foo True
。我们不能指望GHC为我们选择f ~ F, a ~ Int (or Char)
。这将涉及查看所有类型的族,并查看是否可以由它们生成Bool
- 基本上,我们需要反转所有类型族。即使这是可行的,它也会产生大量可能的解决方案,因此它将是模棱两可的。