我有一堆可以处理Vector的函数,即具有类型强制长度的列表。
我正在尝试让我的类型更容易编写,即不是编写
foo :: (Fold Integer v, Map Integer Integer v v, ...) => ...
我正在宣布新课程NList
,所以我可以写foo :: NList v Integer => ...
(简化)类看起来像这样:
class ( Fold (v i) i
, Map i i (v i) (v i)
, Map i (Maybe i) (v i) (v (Maybe i))
) => NList v i
正如您所看到的,我必须将“vector”类型与“item”类型分开(即v
与i
分开),以便我可以执行Map
之类的操作转到Maybe
向量。
因此,v
必须有* -> *
种i
种*
。
然而,当我尝试用这样的向量实例化它时:
instance NList Vec2 Integer
instance NList Vec3 Integer
...
我收到以下错误:
Type synonym `Vec2' should have 1 argument, but has been given none
In the instance declaration for `NList Vec2 Integer'
现在,我对类型级编程非常陌生,而且我知道我很可能以非常落后的方式做这件事。是否可以实例化这样的类型同义词?任何类型向导都有建议更好地实现我的目标吗?
答案 0 :(得分:11)
这里的问题是Vec2
和Vec3
可能被声明为:
type Vec2 a = Vec (S (S Z)) a
type Vec3 a = Vec (S (S (S Z))) a
类型同义词不能被部分应用,因为各种神秘的原因(它们会导致类型级别的lambdas,它会破坏与实例解析和推理相关的各种事情 - 想象一下你是否可以定义type Id a = a
并使其成为Monad
)的实例。
也就是说,你不能让Vec2
成为任何事物的实例,因为你不能使用Vec2
就好像它是一个类型为{{1的完全承诺类型的构造函数}};它实际上是一个只能完全应用的类型级“宏”。
但是,您可以将类型同义词定义为部分应用程序本身:
* -> *
除了允许您的实例外,这些是等效的。
如果您的type Vec2 = Vec (S (S Z))
type Vec3 = Vec (S (S (S Z)))
类型实际上是
Vec3
或类似的,那么你运气不好;如果要提供任何实例,则必须使用type Vec3 a = Cons a (Cons a (Cons a Nil)))
包装器。 (另一方面,您应该能够通过为newtype
和Nil
提供实例来避免直接在这些类型上定义实例,从而允许您使用Cons
作为实例。)
请注意,使用GHC 7.4的新constraint kinds,您可以完全避免使用单独的类型,只需定义约束同义词:
Vec3
就你的方法而言,基本上应该可以正常工作; Vec包使用相同的一般概念。大量的类和大型上下文可能使错误消息非常混乱并减慢编译速度,但这是类型级hackery的本质。但是,如果您将基本type NList v i =
( Fold (v i) i
, Map i i (v i) (v i)
, Map i (Maybe i) (v i) (v (Maybe i))
)
类型定义为通常的GADT:
Vec
然后你根本不需要任何类型类。如果它以某种其他方式定义但仍然在类型级自然上进行参数化,那么您可以用一个替换所有类:
data Vec n a where
Nil :: Vec Z a
Succ :: a -> Vec n a -> Vec (S n) a
这应该允许你完全消除上下文,但是使向量上的操作定义有点棘手。