I have been programming in languages like C and Lisp for several decades and Haskell for several years. Now, to learn more about Haskell's classes and other great more advanced features, such as parallelism, I tried to create a new data type with matrix semantics, currently supported by a copy on top of the Array library type.
I am using the latest Haskell 2013.2.0.0 platform with ghc enabled.
newtype Matrix a = Matrix (Array (Int,Int) a)
(I understand that data will work instead of newtype, but in this case you will get the same semantics with better performance using newtype).
Some simple functions for creating an identical matrix, for example, work very well:
unityMatrix:: (Num a) => Int -> Matrix a unityMatrix d = Matrix $ let b = ((0,0),(d-1,d-1)) in array b [((i,j),if i==j then 1 else 0)|(i,j)<-range b]
Also creates the basic show function:
instance Show a => Show (Matrix a) where show (Matrix x) = let b = bounds x rb = ((fst.fst) b, (fst.snd) b) cb = ((snd.fst) b, (snd.snd) b) in intercalate "\n" [unwords ([show (x!(r,c))|c<-range cb])|r<-range rb]
Then, for regular arithmetic operators to work, I add the following:
instance Num a => Num (Matrix a) where fromInteger x = let b = ((0,0),(0,0)) in Matrix $ array b [((i,j),if i == j then (fromInteger x) else 0)|(i,j)<-range b] (Matrix x) + (Matrix y) = let b = bounds x in if b /= bounds y then error "Unmatched matrix addition" else Matrix $ array b [(ij,x!ij + y!ij)|ij<-range b] signum (Matrix x) = let b = bounds x in Matrix $ array b [(ij,signum (x!ij))|ij<-range b] abs (Matrix x) = let b = bounds x in Matrix $ array b [(ij,abs (x!ij))|ij<-range b] (Matrix x) - (Matrix y) = let b = bounds x in if b /= bounds y then error "Unmatched matrix subtraction" else Matrix $ array b [(ij,x!ij - y!ij)|ij<-range b] (Matrix x) * (Matrix y) = let b = (((fst.fst.bounds) x, (fst.snd.bounds) x),((snd.fst.bounds) y, (snd.snd.bounds) y)) kb = ((snd.fst.bounds) x, (snd.snd.bounds) x) in if kb /= ((fst.fst.bounds) y, (fst.snd.bounds) y) then error "Unmatched matrix multiplication" else Matrix $ array b [((i,j),sum [(x!(i,k)) * (y!(k,j))|k<-range kb])|(i,j)<-range b]
(I apologize if the indentation is screwed here - the actual code is correctly set back and compiled.)
So far, it’s so good, although it’s a little annoying to define the fromInteger function, which actually does not make any sense in the semantics of the matrix, but creating a 1x1 matrix with a value is just as reasonable as everything else.
My problem is trying to get the correct semantics for multiplying a scalar (i.e. type of Num class) with a matrix. By mathematical convention, which simply means multiplication by type with a scalar.
However, no matter what syntax I try, I do not get the correct result. By default, this implementation only promotes, for example, Int to the matrix, using fromInteger, which (if the matrix is already 1x1) results in the error "Unmatched matrix multiplication." I tried all the alternative syntaxes that I can think of in order to define alternative code for this type of multiplication without success, for example:
(Matrix x) * (y::Int) =
or
(Matrix x) * (Num y) =
or
(*):: (Num a) => Matrix -> a -> Matrix
But they all give me various syntax errors.
How do I determine the definition of a scalar by multiplying the matrix so that it does what you expect from it? I believe that enabling the custom security template feature may help, but I'm not quite sure how to use this in this context.
I understand that I could, by a special case, multiply the matrix to allow the multiplication of any matrix with a 1x1 matrix, which I think will work. But this would be: a) inelegant, (b) un-Haskell-y, (c) probably inefficient, since it would take each scalar to be wrapped in a matrix before multiplication and (d) would allow some code (e.g. multiplication of an arbitrary matrix with any 1x1 matrix) to start when it should lead to an error.
I also understand that there are probably excellent Matrix implementations that somehow circumvented this issue. But using them could defeat my goal to learn.