我一直在使用新的DataKinds
扩展名在其类型中编码大小的向量和矩阵。它基本上是这样的:
data Nat = Zero | Succ Nat
data Vector :: Nat -> * -> * where
VNil :: Vector Zero a
VCons :: a -> Vector n a -> Vector (Succ n) a
现在我们想要典型的实例,比如Functor
和Applicative
。Functor
简单:
instance Functor (Vector n) where
fmap f VNil = VNil
fmap f (VCons a v) = VCons (f a) (fmap f v)
但是这个Applicative
实例有一个问题:我们不知道在 pure 中返回什么类型。但是,我们可以根据向量的大小归纳定义实例:
instance Applicative (Vector Zero) where
pure = const VNil
VNil <*> VNil = VNil
instance Applicative (Vector n) => Applicative (Vector (Succ n)) where
pure a = VCons a (pure a)
VCons f fv <*> VCons a v = VCons (f a) (fv <*> v)
然而,即使这个实例适用于所有向量,类型检查器也不知道这一点,所以我们Applicative
每次使用实例时都必须携带约束。
现在,如果这仅适用于Applicative
实例,那将不是问题,但事实证明,在使用此类类型进行编程时,递归实例声明的技巧是必不可少的。例如,如果我们使用 TypeCompose 库将矩阵定义为行向量的向量,
type Matrix nx ny a = (Vector nx :. Vector ny) a
我们必须定义一个类型类并添加递归实例声明来实现转置和矩阵乘法。这导致我们每次使用代码时都必须携带大量的约束,即使实例实际上适用于所有向量和矩阵(使得约束有点无用)。
有没有办法避免必须进行所有这些限制?是否可以扩展类型检查器以检测这种归纳结构?