声明参数化类型同义词的实例

时间:2012-02-15 08:14:57

标签: haskell types

我有一堆可以处理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”类型分开(即vi分开),以便我可以执行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'

现在,我对类型级编程非常陌生,而且我知道我很可能以非常落后的方式做这件事。是否可以实例化这样的类型同义词?任何类型向导都有建议更好地实现我的目标吗?

1 个答案:

答案 0 :(得分:11)

这里的问题是Vec2Vec3可能被声明为:

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))) 包装器。 (另一方面,您应该能够通过为newtypeNil提供实例来避免直接在这些类型上定义实例,从而允许您使用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

这应该允许你完全消除上下文,但是使向量上的操作定义有点棘手。