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