I was a bit surprised when the following code wouldn't compile:
-- Code 1
import Complex
type Velocity = Complex Double
type Force = Complex Double
type FrictionCoeff = Double
frictionForce :: FrictionCoeff -> Velocity -> Force
frictionForce mu vel = mu * vel
The error says
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
So, in short
-- Code 2
let z = 1 :+ 2
z * 3 -- Goes fine.
z * 2.5 -- Goes fine.
z * (2.5 :: Double) -- Explodes.
Complex defines (*) as
instance (RealFloat a) => Num (Complex a) where
(x:+y) * (x':+y') = (x*x'-y*y') :+ (x*y'+y*x')
Why can 3 (Num a => a) and 2.5 (Fractional a => a) be pattern-matched against (x:+y), but a Double cannot ?
First off, the type of the multiplication operator is
(*) :: Num a => a -> a -> a
which means that you can only multiply numbers of the same type, which is why multiplying a Complex Double by a Double won't work.
So why does multiplying a complex number with a decimal literal work?
It works because numeric literals are polymorphic in Haskell, so when you type an integer literal like 42, it really means fromInteger 42. Similarly, decimal literals like 2.3 becomes fromRational (23 % 10). If you examine the types of those functions,
fromInteger :: Num a => Integer -> a
fromRational :: Fractional a => Rational -> a
this means that integer literals can be any numeric type, while decimal literals can be any fractional type. Complex numbers are both, which is why both z * 3 and z * 2.5 work.
When you aren't dealing with literals, you have to convert. For example, your original function can be fixed by writing:
frictionForce :: FrictionCoeff -> Velocity -> Force
frictionForce mu vel = (mu :+ 0) * vel
Finding the appropriate conversion function is easy using Hoogle, since you can search for functions by type. In this case, searching for Double -> Complex Double gives (:+) as the top result.
You cannot multiply a real number with a complex number, even in "real math"; when you want to take 2 * (2 + 3i), what you actually calculate is (2 + 0i) * (2 + 3i). Similarly, in Haskell, when you say:
let z = 1 :+ 2
z * 3
... then 3 gets converted into a Complex Double as well, making the imaginary part zero. This only happens for literal numbers (2, 3.141 etc) because of Haskell's overloaded literal functionality; since literals don't have a default type (they can represent values of any number type), Haskell can say that the 3 has type Complex Double in this context, and appropriate conversion functions are called automatically.
If you want to do this conversion manually, that is, make a complex number out of a real number that is a variable or otherwise already has a different fixed type, you have to use the realToFrac function, which converts any real number into any fractional number (and a Complex counts as a fractional number in this case).
z * realToFrac (2.5 :: Double)
You can of course also manually append :+ 0 if that looks neater to you.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With