6

我试图理解多参数类型类,但我只是没有得到实例声明。我开始尝试为 Vector 类型创建一个 InnerProductSpace 类型类,以便我可以对两个向量执行点积。一开始我只是想看看是否可以将每个向量的第一个元素相乘。这是我的代码

class InnerProductSpace a b c where
dot :: a -> b -> c

data Vector = Vector [Double]
  deriving (Show)

instance InnerProductSpace Vector Vector Double where
  dot (Vector a) (Vector b) = (head a * head b)

尝试使用点函数后的错误是

No instance for (InnerProductSpace Vector Vector c0)
  arising from a use of `dot'
The type variable `c0' is ambiguous
Possible fix: add a type signature that fixes these type variable(s)
Note: there is a potential instance available:
  instance InnerProductSpace Vector Vector Double
    -- Defined at Vector.hs:8:10
Possible fix:
  add an instance declaration for
  (InnerProductSpace Vector Vector c0)
In the expression: dot a b
In an equation for `it': it = dot a b

我做错了什么?谢谢!

4

2 回答 2

7

问题是编译器不知道如何根据它所知道的情况选择正确的实例。如果你尝试类似

vectorA `dot` vectorA

编译器去寻找正确的dot,知道它的类型必须是dot :: Vector -> Vector -> c0. 不幸的是,这本身并没有足够的信息——<code>c0 可以是任何东西,编译器从不认为仅仅因为它只有一个实例,它就一定是正确的(这与开放世界假设有关——那里可能是编译器还没有看到的另一个实例,所以它更愿意抛出一个错误)。您可以通过明确告诉编译器结果应该是什么来摆脱它

vectorA `dot` vectorB :: Double

但这很乏味,并且对于数字类型可能会失败很多,因为这些类型通常也是通用的。例如,Num这里使用了哪个实例?

(vectorA `dot` vectorB) + 3

我们知道它是Double,但编译器无法证明这一点。

一种解决方案是使用 @AndrewC 在评论中建议的类型族。我实际上强烈推荐。您将在野外看到的另一个解决方案是功能依赖关系。这些是这样写的

class InnerProductSpace a b c | a b -> c where
  dot :: a -> b -> c

并转化为对编译器的承诺:“知道a并且b是足够的信息来唯一标识c”。编译器使您保持诚实,并且它会阻止您编写与该承诺相冲突的实例。

instance InnerProductSpace Vector Vector Double where
  dot (Vector a) (Vector b) = (head a * head b)

instance InnerProductSpace Vector Vector Float where
  dot (Vector a) (Vector b) = (head a * head b)

---

/Users/tel/tmp/foo.hs:10:10:
    Functional dependencies conflict between instance declarations:
      instance InnerProductSpace Vector Vector Double
        -- Defined at /Users/tel/tmp/foo.hs:10:10
      instance InnerProductSpace Vector Vector Float
        -- Defined at /Users/tel/tmp/foo.hs:13:10

但是 promise 为编译器提供了足够的信息来解决Double前面示例中的问题。

Main*> (Vector [1,2,3]) `dot` (Vector [2,3,4]) + 3.0
5
于 2013-08-06T23:28:33.287 回答
3

或使用 TypeFamilies

class (D a b ~ c) => InnerProductSpace a b c where
  type D a b
  dot :: a -> b -> c

或者

class InnerProductSpace a b where
  type D a b :: *
  dot :: a -> b -> D a b

instance InnerProductSpace Vector Vector where
  type D Vector Vector = Double
  dot (Vector a) (Vector b) = (head a * head b)
于 2013-08-07T09:56:37.603 回答