我正在尝试在类型级别构建一个列表,但是我在确定如何强制执行约束时遇到了一些麻烦。
我的基本代码是:
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 q
和c~Foo z
,当然我的列表将满足所有BCtx约束水平。
我不确定修复这些特定错误会导致编译代码,但这是一个开始。整个Bar类基本上是固定的(Constraint类是必需的,Bar的实例必须有类型* - > *)。我不相信我可以使用存在类型来创建通用对象列表,因为我需要访问q~QCons q1 (QCons q2 QNil)
参数。我愿意更改qi
和FList
的类型,以允许我在一系列条形上模拟工作。
谢谢你的时间!
答案 0 :(得分:2)
要处理类型列表,必须区分空列表和非空列表并单独处理它们。代码中的“无法推断”错误是因为您的实例假定非空列表,而实际上列表可能为空,也可能不为空。以下是使用扩展程序TypeFamilies
,TypeOperators
,DataKinds
和GADTs
的解决方案。
使用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,用于表达如何仅针对某些类型列表FList
在InjList 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 ++ ")"