我已经定义了以下GADT:
data Vector v where
Zero :: Num a => Vector a
Scalar :: Num a => a -> Vector a
Vector :: Num a => [a] -> Vector [a]
TVector :: Num a => [a] -> Vector [a]
如果不明显,我正在尝试实现一个简单的向量空间。所有向量空间都需要向量添加,所以我想通过Vector
和instance
Num
来实现这一点。在向量空间中,添加不同长度的向量没有意义,这是我想要强制执行的。我认为这样做的一种方法是使用警卫:
instance Num (Vector v) where
(Vector a) + (Vector b) | length a == length b =
Vector $ zipWith (+) a b
| otherwise =
error "Only add vectors with the same length."
这种方法没有什么问题,但我觉得必须有一种方法可以用模式匹配来做到这一点。也许一种方法是定义一个新的数据类型VectorLength
,它看起来像这样:
data Length l where
AnyLength :: Nat a => Length a
FixedLength :: Nat a -> Length a
然后,可以将长度组件添加到Vector
数据类型,如下所示:
data Vector (Length l) v where
Zero :: Num a => Vector AnyLength a
-- ...
Vector :: Num a => [a] -> Vector (length [a]) [a]
我知道这不是正确的语法,但这是我正在玩的一般想法。最后,您可以将添加定义为
instance Num (Vector v) where
(Vector l a) + (Vector l b) = Vector $ zipWith (+) a b
这样的事情是可能的,还是有其他方法可以为此目的使用模式匹配?
答案 0 :(得分:4)
你正在寻找的东西(在这个例子中令人困惑)也被命名为Vector
。通常,这些用于依赖类型的语言,您可以在其中编写类似
data Vec (n :: Natural) a where
Nil :: Vec 0 a
Cons :: a -> Vec n a -> Vec (n + 1) a
但这远非有效的Haskell(或任何语言)。最近对GHC的一些扩展开始实现这种表达,但它们还没有。
您可能对fixed-vector感兴趣,它可以在相对稳定的GHC中对固定Vector
进行最佳近似。它在类型族和continuation之间使用了许多技巧来创建固定大小的向量类。
答案 1 :(得分:4)
只是在另一个答案中添加示例 - 这个几乎已经在GHC 7.6中工作了:
{-# LANGUAGE DataKinds, GADTs, KindSignatures, TypeOperators #-}
import GHC.TypeLits
data Vector (n :: Nat) a where
Nil :: Vector 0 a
Cons :: a -> Vector n a -> Vector (n + 1) a
该代码编写得很好,它并不像你希望的那样工作。让我们在ghci中查看:
*Main> :t Nil
Nil :: Vector 0 a
到目前为止很好......
*Main> :t Cons "foo" Nil
Cons "foo" Nil :: Vector (0 + 1) [Char]
嗯,这有点奇怪......为什么说(0 + 1)
而不是1
?
*Main> :t Cons "foo" Nil :: Vector 1 String
<interactive>:1:1:
Couldn't match type `0 + 1' with `1'
Expected type: Vector 1 String
Actual type: Vector (0 + 1) String
In the return type of a call of `Cons'
In the expression: Cons "foo" Nil :: Vector 1 String
嗯。哎呀。这就是为什么它说(0 + 1)
而不是1
的原因。它不知道那些是相同的。这将在GHC 7.8中得到修复(至少在这种情况下),这应该是......在几个月内,我认为?