1

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?

nclark
  • 1,022
  • 1
  • 11
  • 16
  • Note that Double is a specific instance of Num. `Matrix Double` is another specific instance of Num, but subtraction requires both arguments to be of the same type. – ErikR Aug 18 '16 at 22:12
  • 1
    Notice that `3` actually has the type `Matrix Double` in your example. Numbers are overloaded in Haskell. – Alec Aug 18 '16 at 22:14

2 Answers2

7

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.

Guerric Chupin
  • 461
  • 2
  • 13
  • I'm inferring from your words that what's actually happening when the compiler encounters ``m - 3`` is that it converts the 3 to a Matrix Double of ostensibly the same dimension as the matrix on the left, then performs the math on matrices? (Your point about not being able to subtract scalars from matrices runs sort of counter to the standard practice in many linear algebra packages of allowing just that.) – nclark Aug 18 '16 at 22:14
  • 3
    @nclark: That's exactly what happens. `3` has the type `Num a => a`, based on the `fromInteger` function. `Matrix` has a `Num` instance that implements `fromInteger` as appropriate. – Tikhon Jelvis Aug 18 '16 at 22:17
  • Yep, exactly. It uses the `fromInteger` method from the `Num` class to do that, so your expression becomes `m - fromInteger 3`. In the case of `hmatrix`, I think `fromInteger n` is defined as the matrix where every element equals `n`. – Guerric Chupin Aug 18 '16 at 22:18
  • @nclark: actually, not quite. It converts `3` to a 1 × 1 matrix with the only value 3; then the subtraction operator pretends to expand that to an _n_ × _n_ diagonal matrix to take away from `m`. This is much the same thing Matlab does and isn't as inefficient as actually generating an _n_ × _n_ diagonal matrix, though frankly I consider it semantically rubbish. – leftaroundabout Aug 19 '16 at 09:34
5

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.

Ben
  • 68,572
  • 20
  • 126
  • 174