Haskell 带约束的类型列表

Haskell 带约束的类型列表,haskell,types,type-constraints,constraint-kinds,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 ConstraintK

我正试图在类型级别构建一个列表,但在确定如何强制约束时遇到了一些问题

我的基本代码是:

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
s的类型列表来表示复合数字。我在想象这样的事情:

data QList qi qs = QCons qi qs
                 | QNil
用数据

data FList c q = FNil 
               | FCons (c (Head q)) (FList c (Tail q))
其中
(头q)
应对应于
qi
(尾q)
应对应于
qs
。请注意,
FList
q
参数(不一定)是
(Qux q)
,它是
(Qux qi)
的列表。(我不想对这个列表做更多的充实,因为这是我提出的设计问题之一)。我想在
FList
上“按模数”工作:

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
   -- ...
在GHC中将这些代码片段编译在一起会导致(模转录、抽象和键入错误):

然后

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

我明白为什么会出现这个错误,但不知道如何修复它

具体地说,我期待一个
flistcq
类型,其中
c~fooz
q~qconsq1(qconsq2qnil)
,当然,我的列表将满足各个级别的所有BCtx约束

我不确定修复这些特定错误是否会导致编译代码,但这只是一个开始。整个Bar类基本上是固定的(约束种类是必需的,Bar的实例必须具有种类*->*)。我不相信我可以使用存在类型来创建泛型对象列表,因为我需要访问
qi
参数。我愿意更改
FList
QList
的类型,以允许我在一组条上按模数工作


谢谢你的时间

要处理类型列表,必须区分空列表和非空列表,并分别处理它们。代码中出现“无法推断”错误是因为实例假定列表为非空,而实际上列表可能为空,也可能不为空。下面是一个使用扩展名
TypeFamilies
TypeOperators
datacates
GADTs
的解决方案

使用
数据类型
,类型列表是预定义的。它们有kind
[*]
,但它们将在需要kind
*
的上下文中使用,因此需要一个操作符来强制转换它们:

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,用于表示对于某些类型列表
q'
,如何只在类型
InjList q'
上构造
FList
s。例如,术语
FCons[True]FNil
具有类型
FList[](InjList(Bool':'[])
。另一方面,由于
Bool
不是
InjList q'
的形式,因此没有术语(除了⊥) 类型为
FList[]Bool
。通过对
FList
进行模式匹配,函数可以验证它是否已被赋予非-⊥ 参数,并进一步确定是否已向其传递空类型列表

FList
s的
Bar
实例必须分别处理nil列表和cons列表。nil列表的上下文为空。 cons列表的头和尾都有组件。这是通过关联类型实例
BCtx
中的类型列表上的模式匹配来表示的。函数
f
检查其参数,以验证其是否正确⊥ 并决定它是否为空列表

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)
我们可以将代码加载到GHCi中,以验证其是否有效:

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 ++ ")"

作为上下文约束的一部分,我想我需要使InjList成为Qux的一个实例。但是Qux的参数具有kind*。这可能吗?(这是我所有尝试导致我找到某种类型的嵌套数据的原因之一*)@Eric您可能可以使用
实例Qux(InjList'[])
实例(Qux qi,Qux(InjList qs))=>Qux(InjList(qi):qs))
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 ++ ")"
$ ghci

> :load Test
> f (FCons [1,2] FNil) (FCons [3,4] FNil)
FCons ([4,6]) (FNil)