2

我已经开始使用 Haskell,阅读了一些教程和一本官方书籍 (lyah)。我觉得自己能够开始我的第一个个人项目。至于我选择的任何新语言,我想实现一个用于线性代数处理(围绕矩阵、向量等的操作)的包。功能还可以,但我在数据类型方面并没有走得太远。

最初,我有一个看起来像这样的函数:

     add_vect :: (Num a) => [a] -> [a] -> [a]
     add_vect x y = zipWith (+) x y

现在我想给Vector什么[a]意思命名(),这样vect_add看起来像:

    vect_add :: Vector -> Vector -> Vector
    vect_add x y = zipWith (+) x y

经过多次雄心勃勃的尝试,我最终(受 定义的启发String)得到了一个非常简单的定义:

    type Vector = [Int] 

这样做的问题是我失去了我的函数的类型通用性,它现在只能工作[Int]而不是任何数字类型。

我的问题是:有没有办法将通用性(例如使用类型类)表达到新类型的定义中。类似于:

    type Vector = (Num a) => [a]

或者可能有任何其他方式来保持我Vector的通用性?

4

3 回答 3

3

出色地...

{-# LANGUAGE RankNTypes     #-}

type Vector = forall a . Num a => [a]

但这并不是你真正想要的:你最终会得到

addVect :: (forall a.Num a => [a]) -> (forall b.Num b => [b]) -> (forall c.Num c => [c])

无法定义(每个向量可能有不同的数值类型)。

正如 Paul Johnson 所说,您可以使用type Vector a = [a](或等效地,type Vector = [])。但我认为这并不是您真正想要的:您最终得到的签名与现在相同,并且在跨越向量空间的字段上具有参数多态的向量函数并不是很自然的。

正确的解决方案,IMO,是包装所采用的解决vector-space方案:(简化)

class VectorSpace v where
  type Scalar v :: *     -- This is going to be the type of the
                         -- field ("of the components of each vector")

  (^+^) :: v -> v -> v   -- You call this `addVect`
  (*^)  :: (Scalar v) -> v -> v

  ...

然后你可以有例如

data Vectorℝ2 = Vectorℝ2 !Double !Double

instance VectorSpace Vectorℝ2 where
  type Scalar Vectorℝ2 = Double
  Vectorℝ2 x y ^+^ Vectorℝ2 x' y' = Vectorℝ2 (x+x') (y+y')
  ...

或者

newtype Vectorℝn = Vectorℝn [Double]

instance VectorSpace Vectorℝn where
  type Scalar Vectorℝn = Double
  Vectorℝn xs ^+^ Vectorℝn x's = Vectorℝn $ zipWith (+) xs x's

顺便说一句,zipWith(+)对于可变维向量加法来说,这并不是一个很好的定义:你会得到例如

[1,2,3] ^+^ [4,5]  ≡ [5,7]

虽然实际上我希望[4,5] ≅ [4,5,0]在矢量意义上,因此[1,2,3] ^+^ [4,5] ≡ [5,7,3] ≆ [5,7].

于 2013-07-26T11:40:33.700 回答
3

你不能做你想做的事,因为类型检查器无法知道所有三个向量都是相同的类型。否则,您可以编写如下代码:

mkVect :: [a] -> Vector   -- You are going to need something with this type.


x1 :: [Int]
x1 = [1,2,3]
x2 :: [Double]
x2 = [1.0,2.0,3.0]

v3 = add_vect (mkVect x1) (mkVect x2)

类型检查器可以阻止这种情况的唯一方法是将 Vector 的类型参数作为 add_vect 的一部分。

所以你必须写

type Vector a = [a]

这样类型检查器就可以看到你在做什么。

于 2013-07-26T11:05:43.923 回答
0

一种“正确”的方法是只注意您仅Vector用于文档目的,因此type Vector a = [a]并不是一件坏事。然后你有vect_add :: Vector a -> Vector a -> Vector awhich must match 甚至vector-space's (*^) :: a -> Vector a -> Vector a

于 2013-07-26T14:10:34.553 回答