简体   繁体   中英

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 .

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 aaaa , 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 abcd . 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 abcd , 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM