类型族和部分新类型之间的区别? (和部分数据?)

时间:2017-09-07 20:23:27

标签: haskell gadt type-families

我必须连接两个库,其中元数据在一个中表示为类型参数,在另一个中表示为记录字段。我用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'
  • 的数据类型声明中   

好吧,如果这样说......

我不确定我理解为什么会产生这样的差异。没有使用类型系列没有应用它们?这是怎么回事?还有其他(更好)的方法吗?

1 个答案:

答案 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)具有类型Intfoo (... :: F Int)相当于foo (... :: Bool),可能有Int类型,或类型Char - 它是一个含糊不清的类型。

另请考虑foo True。我们不能指望GHC为我们选择f ~ F, a ~ Int (or Char)。这将涉及查看所有类型的族,并查看是否可以由它们生成Bool - 基本上,我们需要反转所有类型族。即使这是可行的,它也会产生大量可能的解决方案,因此它将是模棱两可的。