当我在左边放置一个 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 Double
做 m - 3
时会发生什么 3 :: Matrix Double
。 Matrix Double
是 Num
的实例这一事实意味着编译器知道如何翻译乱码 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
断言和减法运算符获得两个相同类型的参数的要求),所以你得到一个类型错误。
由于 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 Double
做 m - 3
时会发生什么 3 :: Matrix Double
。 Matrix Double
是 Num
的实例这一事实意味着编译器知道如何翻译乱码 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
断言和减法运算符获得两个相同类型的参数的要求),所以你得到一个类型错误。