当以下代码无法编译时,我有点意外:
-- Code 1
import Complex
type Velocity = Complex Double
type Force = Complex Double
type FrictionCoeff = Double
frictionForce :: FrictionCoeff -> Velocity -> Force
frictionForce mu vel = mu * vel
错误说
Couldn't match expected type `Complex Double'
with actual type `Double'
Expected type: Force
Actual type: FrictionCoeff
In the first argument of `(*)', namely `mu'
In the expression: mu * vel
所以,简而言之
-- Code 2
let z = 1 :+ 2
z * 3 -- Goes fine.
z * 2.5 -- Goes fine.
z * (2.5 :: Double) -- Explodes.
复杂定义(*)为
instance (RealFloat a) => Num (Complex a) where
(x:+y) * (x':+y') = (x*x'-y*y') :+ (x*y'+y*x')
为什么3(Num a => a)和2.5(Fractional a => a)可以与(x:+ y)进行模式匹配,但Double不能?
答案 0 :(得分:15)
首先,乘法运算符的类型是
(*) :: Num a => a -> a -> a
表示您只能将相同类型的数字相乘,这就是为什么将Complex Double
乘以Double
将无效。
那么为什么将复数与十进制文字相乘呢?
它的工作原理是因为数字文字在Haskell中是多态的,所以当你输入像42
这样的整数文字时,它实际上意味着fromInteger 42
。同样,像2.3
这样的小数文字也会变成fromRational (23 % 10)
。如果检查这些函数的类型,
fromInteger :: Num a => Integer -> a
fromRational :: Fractional a => Rational -> a
这意味着整数文字可以是任何数字类型,而十进制文字可以是任何小数类型。复数都是,这就是z * 3
和z * 2.5
都可以工作的原因。
当你不处理文字时,你必须转换。例如,您可以通过写入来修复原始函数:
frictionForce :: FrictionCoeff -> Velocity -> Force
frictionForce mu vel = (mu :+ 0) * vel
使用Hoogle可以轻松找到合适的转换函数,因为您可以按类型搜索函数。在这种情况下,搜索Double -> Complex Double
会将(:+)
作为最高结果。
答案 1 :(得分:7)
即使在“真实数学”中,也不能将实数与复数相乘;当您想要2 * (2 + 3i)
时,您实际计算的是(2 + 0i) * (2 + 3i)
。同样,在Haskell中,当你说:
let z = 1 :+ 2
z * 3
...然后3也被转换为Complex Double
,使虚部为零。这只发生在文字数字(2
,3.141
等)上,因为Haskell的重载文字功能;由于文字没有默认类型(它们可以表示任何数字类型的值),Haskell可以说3
在此上下文中具有类型Complex Double
,并且自动调用适当的转换函数。 / p>
如果你想手动进行这种转换,也就是说,从一个变量的实数中得到一个复数,或者已经有一个不同的固定类型,你必须使用realToFrac
函数,将任何实数转换为任何小数(在这种情况下,Complex
计为小数。)
z * realToFrac (2.5 :: Double)
如果看起来比较适合您,您当然也可以手动附加:+ 0
。