由于hmatrix为Matrix类型提供了Num的实例,我可以表达元素减法,如:
m = (2><2)[1..] :: Double Matrix
m' = m - 3
Run Code Online (Sandbox Code Playgroud)
这很好,就像3a一样Num,并且通过从每个元素中减去3来创建矩阵m.
为什么这不起作用:
m' = m - (3::Double)
Run Code Online (Sandbox Code Playgroud)
我得到的错误是:
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)
Run Code Online (Sandbox Code Playgroud)
我希望编译器能够理解a Double也是一个Num.为什么看似不是这样?
当你这样做会发生什么m - 3用m :: Matrix Double的是3 :: Matrix Double.事实Matrix Double是一个实例Num意味着编译器知道如何翻译litteral 3.但是,当您这样做时m - (3 :: Double),会出现类型错误(-) :: (Num a) => a -> a -> a,因此,您减去的元素的类型必须是Num和匹配的实例.因此你可以减去两个Doubles,两个Matrix Doubles而不是a Matrix Double和a Double.
毕竟,这对我来说似乎是合乎逻辑的,减去矩阵和标量是没有意义的.
这是对Haskell基于类型的重载类型的新手的常见误解,特别是那些习惯于在流行的OO语言中使用的基于子类的重载的人.
减法运算符有类型Num a => a -> a -> a; 所以它需要类型类中任何类型的两个参数Num.它看起来就像当你做什么,正在发生m - 3的是减法运算符接受一个Matrix Double在左边和右边一些简单的数字类型.但这实际上是不正确的.
当像一个类型签名Num a => a -> a -> a使用相同类型的变量多次,你可以挑选你喜欢的(之前受contstraints任何类型=>:Num a在这种情况下)来使用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断言和减法运算符得到两个相同类型的参数的要求),所以你得到一个类型错误.