当我在左边放置一个 Double Matrix 并在右边放置一个 Double Matrix 时,为什么 (-) 无法进行类型检查?

Why does (-) fail to typecheck when I place a Double Matrix on the left and a Double on the right?

由于 hmatrix 为 Matrix 类型提供了一个 Num 实例,我可以像这样表达元素减法:

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

效果很好,因为 3 是一个 Num,并通过从 m 的每个元素中减去 3 创建一个矩阵。

为什么这不起作用:

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)

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

当你用 m :: Matrix Doublem - 3 时会发生什么 3 :: Matrix DoubleMatrix DoubleNum 的实例这一事实意味着编译器知道如何翻译乱码 3。但是,当您执行 m - (3 :: Double) 时,您会收到类型错误,因为 (-) :: (Num a) => a -> a -> a,因此您减去的元素类型必须是 Num 的实例并匹配。因此,您可以减去两个 Double、两个 Matrix Double,但不能减去一个 Matrix Double 和一个 Double.

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

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

减法运算符的类型为 Num a => a -> a -> a;所以它需要两个 class Num 类型的任何类型的参数。它 看起来 就像当你做 m - 3 时发生的事情是减法运算符在左边接受一个 Matrix Double 而在右边接受一些简单的数字类型。但这实际上是不正确的。

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

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

的方式

但是如果你使用 m' = m - (3::Double) 那么你不会让它只是弄清楚 3 必须要使用什么类型才能使减法运算符有效,你是在告诉它这3 具体是 Double两个事实不可能都是真的(你的:: Double断言和减法运算符获得两个相同类型的参数的要求),所以你得到一个类型错误。