Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multiplying a Complex Double with a Double in Haskell

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 ?

like image 517
Niriel Avatar asked Mar 28 '12 11:03

Niriel


2 Answers

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.

like image 97
hammar Avatar answered Oct 27 '22 11:10

hammar


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.

like image 45
dflemstr Avatar answered Oct 27 '22 10:10

dflemstr