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

Haskell 声明参数化类型同义词的实例,haskell,types,Haskell,Types,我有一系列处理向量的函数,即具有类型强制长度的列表 我正在努力使我的打字更容易写,也就是说,代替写作 foo :: (Fold Integer v, Map Integer Integer v v, ...) => ... 我正在声明一个新类NList,这样我就可以编写foo::nlistv Integer=>… (简化)类如下所示: class ( Fold (v i) i , Map i i (v i) (v i) , Map i (Maybe i) (v i

我有一系列处理向量的函数,即具有类型强制长度的列表

我正在努力使我的打字更容易写,也就是说,代替写作

foo :: (Fold Integer v, Map Integer Integer v v, ...) => ...
我正在声明一个新类
NList
,这样我就可以编写
foo::nlistv 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
type Vec2 a = Vec (S (S Z)) a
type Vec3 a = Vec (S (S (S Z))) a
如您所见,我必须将“vector”类型与“item”类型分开(即
v
I
分开),这样我就可以将
映射到
可能的
向量上

因此,
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'

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

这里的问题是
Vec2
Vec3
可能是这样声明的:

class ( Fold (v i) i
      , Map i i (v i) (v i)
      , Map i (Maybe i) (v i) (v (Maybe i))
      ) => NList v i
type Vec2 a = Vec (S (S Z)) a
type Vec3 a = Vec (S (S (S Z))) a
由于各种神秘的原因,不能部分应用类型同义词(它们会导致类型级别的lambda,这会破坏与实例解析和推理相关的各种事情-想象一下,如果您可以定义
Type Id a=a
,并使其成为
Monad
的实例)

也就是说,您不能将
Vec2
作为任何事物的实例,因为您不能将
Vec2
当作一个具有
*->*
类型的成熟类型构造函数来使用;它实际上是一个只能完全应用的类型级“宏”

但是,您可以将类型同义词定义为部分应用程序本身:

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
提供实例,允许您将
Vec3
用作实例,您应该能够避免直接在这些类型上定义实例。)

请注意,使用GHC 7.4的新版本,您可以完全避免单独的类型,只需定义一个约束同义词:

就你的方法而言,它基本上可以正常工作;包中使用了相同的总体思路。大量的类和大量的上下文可能会使错误消息变得非常混乱并减慢编译速度,但这就是类型级黑客行为的本质。但是,如果将基本
Vec
类型定义为常规GADT:

data Vec n a where
    Nil :: Vec Z a
    Succ :: a -> Vec n a -> Vec (S n) a
那么实际上您根本不需要任何类型类。如果它是以其他方式定义的,但仍然在类型级别natural上参数化,则可以用一个类替换所有类:

data Proxy s = Proxy

class Nat n where
    natElim
        :: ((n ~ Z) => r)
        -> (forall m. (n ~ S m, Nat m) => Proxy m -> r)
        -> Proxy n
        -> r

这将允许您完全消除上下文,但会使定义向量上的操作变得有点棘手。

非常酷,谢谢。我不明白的是最后一个例子。tilda接线员怎么了?这是一个。如果范围中有
Num a
约束,则可以对
a
类型的值使用算术运算符;如果范围中有
(a~b)
约束,则可以将
a
值用作
b
值,反之亦然。基本上,你可以把
(n~Z)=>r
理解为“向我证明
n
Z
,我会给你一个
r
”。