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)