1

由于 hmatrix 为 Matrix 类型提供了 Num 的实例,我可以表达元素减法,例如:

m = (2><2)[1..] :: Double Matrix
m' = m - 3

这很好用,就像3a一样Num,并且通过从 的每个元素中减去 3 来创建一个矩阵m

为什么这也不起作用:

m' = m - (3::Double)

我得到的错误是:

Couldn't match expected type ‘Matrix Double’
            with actual type ‘Double’
In the second argument of ‘(-)’, namely ‘(3 :: Double)’
In the expression: m - (3 :: Double)

我希望编译器能够理解 aDouble也是Num. 为什么看起来不是这样?

4

2 回答 2

7

当你这样做时会m - 3发生m :: Matrix Double什么3 :: Matrix DoubleMatrix Double这是一个实例的事实Num意味着编译器知道如何翻译 litteral 3。但是,当你这样做时m - (3 :: Double),你会得到一个类型错误,因为(-) :: (Num a) => a -> a -> a,所以你减去的元素的类型必须是实例Num并且匹配。因此,您可以减去两个Doubles,两个Matrix Doubles 但不能减去 aMatrix Double和 a Double

毕竟,这对我来说似乎相当合乎逻辑,减去矩阵和标量是没有意义的。

于 2016-08-18T22:07:05.273 回答
5

这是对 Haskell 基于类型类重载风格的新手的常见误解,尤其是那些习惯于流行 OO 语言中使用的基于子类的重载的人。

减法运算符的类型为Num a => a -> a -> a; 所以它需要两个类型类中的任何类型的参数Num。当您这样做时,似乎发生的事情是减法m - 3运算符Matrix Double在左侧接受 a ,在右侧接受一些简单的数字类型。但这实际上是不正确的。

当一个类型签名像Num a => a -> a -> a多次使用相同的类型变量时,您可以选择您喜欢的任何类型(在这种情况下,受=>:之前的约束Num a)来使用,a但它必须是所有出现的完全相同的类型aMatrix Double -> Double -> ???不是类型的有效实例化Num a => a -> a -> a(如果是,你怎么知道它返回了什么?)。

有效的原因m - 3因为两个参数必须是相同的类型,并且m肯定是 type Matrix Double,编译器看到它3也必须是 type Matrix Double。因此,它不是使用3源文本中出现的来构建一个Double(或Integer,或许多其他数字类型之一),而是使用源文本3来构建一个Matrix Double。实际上,类型推断改变了编译器读取源代码文本的方式3

但是,如果您使用m' = m - (3::Double),那么您不会让它仅仅弄清楚3必须使用什么类型才能使减法运算符有效,您是在告诉它这3是专门的Double. 不可能两个事实都是正确的(您的:: Double断言和减法运算符获取相同类型的两个参数的要求),因此您会收到类型错误。

于 2016-08-19T03:56:22.683 回答