nclark
nclark

Reputation: 1082

Why does (-) fail to typecheck when I place a Double Matrix on the left and a Double on the right?

Since hmatrix provides an instance of Num for Matrix types, I can express element-wise subtraction like:

m = (2><2)[1..] :: Double Matrix
m' = m - 3

That works great, as 3 is a Num, and results in a matrix created by subtracting 3 from each element of m.

Why does this not also work:

m' = m - (3::Double)

The error I'm getting is:

Couldn't match expected type ‘Matrix Double’
            with actual type ‘Double’
In the second argument of ‘(-)’, namely ‘(3 :: Double)’
In the expression: m - (3 :: Double)

I expected the compiler to understand that a Double is also a Num. Why is that seemingly not the case?

Upvotes: 1

Views: 90

Answers (2)

Ben
Ben

Reputation: 71495

This is a common misunderstanding of people new to Haskell's style of typeclass based overloading, especially those who are used to the subclass-based overloading used in popular OO languages.

The subtraction operator has type Num a => a -> a -> a; so it takes two arguments of any type that is in the type class Num. It seems like what is happening when you do m - 3 is that the subtraction operator is accepting a Matrix Double on the left and some simple numeric type on the right. But that is actually incorrect.

When a type signature like Num a => a -> a -> a uses the same type variable multiple times, you can pick any type you like (subject to the contstraints before the =>: Num a in this case) to use for a but it has to be the exact same type everywhere that a appears. Matrix Double -> Double -> ??? is not a valid instantiation of the type Num a => a -> a -> a (and if it were, how would you know what it returned?).

The reason m - 3 works is that because both arguments have to be the same type, and m is definitely of type Matrix Double, the compiler sees that 3 must also be of type Matrix Double. So instead of using the 3 appearing in the source text to build a Double (or Integer, or one of many other numeric types), it uses the source text 3 to build a Matrix Double. Effectively the type inference has changed the way the compiler reads the source code text 3.

But if you use m' = m - (3::Double) then you're not letting it just figure out what type 3 must have to make the use of the subtraction operator valid, you're telling it that this 3 is specifically a Double. There's no way both facts to be true (your :: Double assertion and the requirement that the subtraction operator gets two arguments of the same type), so you get a type error.

Upvotes: 5

Guerric Chupin
Guerric Chupin

Reputation: 461

What happens when you do m - 3 with m :: Matrix Double is that 3 :: Matrix Double. The fact that Matrix Double is an instance of Num means that the compilers knows how to translate the litteral 3. However when you do m - (3 :: Double), you get a type error because (-) :: (Num a) => a -> a -> a, so the type of the element you subtract must be instances of Num and match. Hence you can subtract two Doubles, two Matrix Doubles but not a Matrix Double and a Double.

After all, this seems fairly logical to me, it doesn't make sense to subtract a matrix and a scalar.

Upvotes: 7

Related Questions