fdsa
fdsa

Reputation: 1409

Haskell Matrix appears to not be calling Num instance

I am trying to multiply 2x2 matrices as a learning exercise. I am trying to make my Matrix type an instance of Num so I can multiply with *. When I attempt to execute (Matrix 1 2 3 4) *(Matrix 1 1 1 1) in GHCI, I get an error that says use FlexibleContexts to permit this even though I am using Flexible contexts:

{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
import Debug.Trace
data Matrix a b c d= Matrix a b c d
    deriving (Show)

instance Num a=>Num (Matrix a a a a) where
    (*) (Matrix a1 b1 c1 d1) (Matrix a2 b2 c2 d2) =  (trace "* ran")(Matrix (a1*a2 + b1*c2) (a1*b2 + b1*d2) (c1*a2 + d1*c2) (c1*b2+d2*d1))

multMat (Matrix a1 b1 c1 d1) (Matrix a2 b2 c2 d2) =  (trace "Multmat ran")(Matrix (a1*a2 + b1*c2) (a1*b2 + b1*d2) (c1*a2 + d1*c2) (c1*b2 + d1*d2))

I don't think my * method is being called as the "* ran" never prints out. On the other hand, if I run multMat (which is the same code, except for the function name) the debug statement prints and I get the expected answer.

*I think the problem is that when is use * on Matrix data types, some other method is being called. How can I make it call my method? *

While this code compiles, it gives a runtime error on attempts to use * type Matrix.

Upvotes: 1

Views: 192

Answers (1)

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 476739

The definition for your matrix multiplication is fine (although semantically it is not correct). Your code compiles well. The problem is however that if you perform a query:

*Main> (Matrix 1 2 3 4) * (Matrix 7 5 3 1)

Haskell is confused which type it should attach to 1, 2, etc. It could be Int, Integer, Float, Double, etc. and all these have different implementations (if it picks Float, it will perform a floating point multiplication, which is different from an integer multiplication).

The solution is that you give Haskell a hint which type you are using:

*Main> (Matrix (1 :: Int) (2 :: Int) (3 :: Int) (4 :: Int)) * (Matrix (7 :: Int) (5 :: Int) (3 :: Int) (1 :: Int))
Matrix 7 6 15 4

After all, 1 can be any kind of Num type (even a matrix), internally Haskell will call fromInteger to convert it to the correct Num type.

That's also the reason why instantiating a Matrix from Num is not straightforward: you need to convert an integer to a matrix. Since your Matrix datatype only allows 2 x 2 matrices, there is no straightforward way of doing so.

A final note is that your matrix multiplication is wrong. You should use:

instance Num a=>Num (Matrix a a a a) where
    (*) (Matrix a1 b1 c1 d1) (Matrix a2 b2 c2 d2) = Matrix (a1*a2+b1*c2) (a1*b2+b1*d2) (c1*a2+d1*c2) (b2*c1+d1*d2)

although I can understand this is only a simple test.

Additional notes

In the comments, @stites asked an interesting question of whether it would be sufficient to instantiate only one of the Matrix types. Something like:

(Matrix (1 :: Int) 2 3 4) * (Matrix 7 5 3 1)

Haskell however errors on this, and that is reasonable. After all, a valid way of reasoning about this, is that you can still intend to use:

Matrix Int Float Float Int

Now you only defined an instance for Matrix a a a a, but it would be possible to define an additional one, like:

instance Num (Matrix Int Float Float Int) where
    (*) ...

since a compiler should be conservative, it cannot simply assume the most popular instance is selected: perhaps you are under the impression that you have defined another instance somewhere.

Additionally when you fully specify the type of the left matrix, it works:

*Main> (Matrix (1 :: Int) (2 :: Int) (3 :: Int) (4 :: Int)) * (Matrix 7 5 3 1)
Matrix 7 6 15 4

This is because now you have fully specified the left operand being of the type Matrix Int Int Int Int. Since (*) has the signature:

(*) :: Num e => e -> e -> e

(used e to reduce confusion), it knows that e = Matrix Int Int Int Int so it can "collapse" the right operand: it can thus derive the right operand is of type Matrix Int Int Int Int as well.

You can even specify the types at arbitrary sides, for instance:

*Main> (Matrix (1 :: Int) 2 (3 :: Int) 4) * (Matrix 7 (5 :: Int) 3 (1 :: Int))
Matrix 7 6 15 4

works as well. Because first Haskell derives that e = Matrix a b c d. Now the first operand fills in a = Int, the second b = Int, etc.

@user2407038 also provides a useful comment. You can also write your defintion as:

{-# LANGUAGE GADTs #-}

-- ...

instance (Num a, a ~ b, b ~ c, c ~ d) => Num (Matrix a b c d) where
    (*) (Matrix a1 b1 c1 d1) (Matrix a2 b2 c2 d2) = Matrix (a1*a2+b1*c2) (a1*b2+b1*d2) (c1*a2+d1*c2) (b2*c1+d1*d2)

Since the signature says Matrix a b c d, Haskell will try to match the multipication with this instance. And it can do so if (Num a, a ~ b, b ~ c, c ~ d). Which is true.

Upvotes: 3

Related Questions