Reputation: 1409
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
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.
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