Nir*_*iel 7 haskell pattern-matching
当下面的代码无法编译时,我有点惊讶:
-- 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不能?
ham*_*mar 15
首先,乘法运算符的类型是
(*) :: Num a => a -> a -> a
这意味着你只能乘以相同类型的数字,这就是为什么乘以Complex Doublea 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给出(:+)最佳结果.
即使在"真实数学"中,也不能将实数与复数相乘; 当你想要2 * (2 + 3i),你实际计算的是(2 + 0i) * (2 + 3i).同样,在Haskell中,当你说:
let z = 1 :+ 2
z * 3
...然后将3转换为a Complex Double,使虚部成为零.这仅发生于文字数字(2,3.141因为Haskell的重载文字功能等); 由于文字没有默认类型(它们可以表示任何数字类型的值),Haskell可以说在此上下文中3有has类型Complex Double,并且自动调用适当的转换函数.
如果你想手动进行这种转换,也就是说,从一个变量的实数中得到一个复数,或者已经有一个不同的固定类型,你必须使用该realToFrac函数,它将任何实数转换成任何小数(Complex在这种情况下,计数为小数).
z * realToFrac (2.5 :: Double)
:+ 0如果看起来更整洁,你当然也可以手动追加.