键入带有约束的列表

时间:2013-01-10 03:06:26

标签: haskell types type-constraints constraint-kinds

我正在尝试在类型级别构建一个列表,但是我在确定如何强制执行约束时遇到了一些麻烦。

我的基本代码是:

data Foo z q = Foo1 (z q)
             | Foo2 (z q)

class Qux q -- where ...
class Baz z -- where ...

class Bar a where             -- a has kind *->*
  type BCtx a q :: Constraint -- using ConstraintKinds to allow constraints on the concrete type
  f :: (BCtx a q) => a q -> a q -> a q
  g :: (BCtx a q, BCtx a q') => a q -> a q'

instance (Baz z) => Bar (Foo z) where
  type BCtx (Foo z) q = (Num (z q), Qux q) -- for example
  f (Foo1 x) (Foo1 y) = Foo1 $ x+y -- these functions need access to the type q to do arithmetic mod q
  f (Foo1 x) (Foo2 y) = Foo2 $ x-y
  -- ...

你可以想到上面代表主要权力的q s。我还想使用qi的类型列表来表示复合数字。我想象的是:

data QList qi qs = QCons qi qs
                 | QNil

带有数据

data FList c q = FNil 
               | FCons (c (Head q)) (FList c (Tail q))

其中(Head q)应与qi对应,而(Tail q)应与qs对应。请注意,q的{​​{1}}参数不是(必须)FList,而是(Qux q)列表。 (我不想充实关于此列表的任何内容,因为这是我正在构建的设计问题之一)。我想在(Qux qi)

上以“模数方式”工作
FList

在GHC结果中编译这些代码片段(模数转录,抽象和输入错误):

instance (Bar c) => Bar (FList c) where
   type BCtx (FList c) q = () -- Anything I put here is not enough
   f (FCons x xs) (FCons y ys) = FCons (f x y) (f xs ys)
   -- the left call to `f` calls a concrete instance, the right call to `f` is a recursive call on the rest of the list
   -- ...

然后

Could not deduce (BCtx c (Head q), BCtx c (Tail q))

我明白为什么我会收到此错误,但不知道如何修复错误。

具体来说,我期待Could not deduce (BCtx c (Head (Tail q)), BCtx c (Tail (Tail q))) 类型FList c qc~Foo z,当然我的列表满足所有BCtx约束水平。

我不确定修复这些特定错误会导致编译代码,但这是一个开始。整个Bar类基本上是固定的(Constraint类是必需的,Bar的实例必须有类型* - > *)。我不相信我可以使用存在类型来创建通用对象列表,因为我需要访问q~QCons q1 (QCons q2 QNil)参数。我愿意更改qiFList的类型,以允许我在一系列条形上模拟工作。

谢谢你的时间!

1 个答案:

答案 0 :(得分:2)

要处理类型列表,必须区分空列表和非空列表并单独处理它们。代码中的“无法推断”错误是因为您的实例假定非空列表,而实际上列表可能为空,也可能不为空。以下是使用扩展程序TypeFamiliesTypeOperatorsDataKindsGADTs的解决方案。

使用DataKinds,预定义了类型列表。它们有[*]种类,但它们将在期望种类*的上下文中使用,因此需要运算符来强制转换它们:

data InjList (qs :: [*])

使用类型列表,FList定义为

data FList c q where
  FNil :: FList c (InjList '[])
  FCons :: c h -> FList c (InjList t) -> FList c (InjList (h ': t))

它被定义为GADT,用于表达如何仅针对某些类型列表FListInjList q'类型上构建q'。例如,术语FCons [True] FNil的类型为FList [] (InjList (Bool ': '[]))。另一方面,由于Bool不是InjList q'形式,因此类型FList [] Bool没有术语(除了⊥)。通过FList上的模式匹配,函数可以验证它是否已被赋予非⊥参数,并进一步确定它是否已被传递为空类型列表。

Bar的{​​{1}}实例必须单独处理nil列表和cons列表。零列表具有空的上下文。  缺点列表包含列表头部和尾部的组件。这通过FList的关联类型实例中的类型列表上的模式匹配来表示。函数BCtx检查其参数以验证它不是⊥并确定它是否为空列表。

f

我们可以将代码加载到GHCi中以验证它是否有效:

instance (Bar c) => Bar (FList c) where
  -- Empty context for '[]
  type BCtx (FList c) (InjList '[]) = ()
  -- Context includes components for head and tail of list
  type BCtx (FList c) (InjList (h ': t)) = (BCtx c h, BCtx (FList c) (InjList t))

  f FNil FNil = FNil
  f (FCons x xs) (FCons y ys) = FCons (f x y) (f xs ys)
instance Bar [] where
  type BCtx [] q = Num q
  f xs ys = zipWith (+) xs ys

instance Show (FList c (InjList '[])) where
  show FNil = "FNil"

instance (Show (c h), Show (FList c (InjList t))) => Show (FList c (InjList (h ': t))) where
  show (FCons h t) = "FCons (" ++ show h ++ ") (" ++ show t ++ ")"