I'm trying to implement a function that multiplies polynomials (represented using lists -- 3x^2 + 5x + 2 = P [2,5,3]):
newtype Poly a = P [a]
plus :: Num a => Poly a -> Poly a -> Poly a
plus (P a) (P b) = P (map (\(y,z) -> z + y) (zipWithPadding 0 a b))
where
zipWithPadding :: (Num a) => a -> [a] -> [a] -> [(a, a)]
zipWithPadding e (aa: as) (bb: bs) = ((aa, bb): zipWithPadding e as bs)
zipWithPadding e [] bs = zip (repeat e) bs
zipWithPadding e as [] = zip as (repeat e)
times :: Num a => Poly a -> Poly a -> Poly a
times (P a) (P b) = sum $ multList 0 [] a b
where
multList :: Num a => Int -> [Poly a] -> [a] -> [a] -> [Poly a]
multList _ s [] _ = s
multList e s (aa:as) bs = multList (e + 1) (s ++ (multElement e aa bs)) as bs
multElement :: Num a => Int -> a -> [a] -> [Poly a]
multElement e aa bs = [P $ replicate e 0 ++ (map (*aa) bs)]
instance Num a => Num (Poly a) where
(+) = plus
(*) = times
negate = undefined
fromInteger = undefined
-- No meaningful definitions exist
abs = undefined
signum = undefined
When I tried to run however, I got an undefined error:
*HW04> times (P [1,2,2]) (P [1,2])
*** Exception: Prelude.undefined
I'm confused.
Clearly you are calling one of the undefined methods in the Num instance for Poly.
You can determine which one is being called by using these definitions:
negate = error "Poly negate undefined"
fromInteger = error "Poly fromInteger undefined"
abs = error "Poly abs undefined"
signum = error "Poly signum undefined"
Running your test expression yields:
Poly *** Exception: Poly fromInteger undefined
The problem is in your use of sum which is essentially defined as:
sum xs = foldl (+) 0 xs
It is therefore calling fromInteger 0. You can fix this with:
fromInteger x = P [ fromInteger x ]
Update
The reason fromInteger for Poly a needs to be defined this way is
because we need to construct a list of Num a values, and fromInteger x
is the way to create a Num a from the Integer value x.
A polynomial is not really a Num, although there is a ring monomorphism Num a => a -> Poly a.
Discard that Num instance and use foldl plus instead of sum.
I'm going to take the position that you should not define an instance of a class simply to hijack the class's functions. The minimal definition of a Num instance expects certain functions to be defined; explicitly assigning undefined to those names does not qualify as a definition. Consider that Haskell provides a specific operator (++) for list concatenation instead of simply overloading (+) with an instance like
instance Num [a] where
a + [] = a
[] + b = b
(a:as) + b = a:(as + b)
(*) = undefined
negate = undefined
-- etc
Instead, define a class that does provide the operations you want. In this case, you want a Ring, which is a type along with two operations, addition and multipication, that obey certain laws. (Put briefly, the operations act as you would expect given the integers as an example, except multiplication is not required to be commutative.)
In Haskell, we would define the class as
class Ring a where
rplus :: a -> a -> a -- addition
rmult :: a -> a -> a -- multiplication
rnegate :: a -> a -- negation
runit :: a -- multiplicative identity
rzero :: a -- additive identity, multiplicative zero
Any value with a valid Num instance forms a ring, although you need to define the instances separately.
instance Ring Integer where
rplus = (+)
rmult = (*)
rnegate = negate
rzero = 0
runit = 1
instance Ring Float
rplus = (+)
rmult = (*)
rnegate = negate
rzero = 0
runit = 1
-- etc
You can define an instance of Ring for polynomials, as long as the coefficients form a ring as well.
newtype Poly a = P [a]
instance Ring a => Ring (Poly a) where
-- Take care to handle polynomials with different degree
-- Note the use of rplus and rzero instead of (+) and 0
-- when dealing with coefficients
rplus (P a) (P b) = case (compare (length a) (length b)) of
LT -> rplus (P (rzero:a)) (P b)
EQ -> P $ zipWith rplus a b
GT -> rplus (P a) (P (rzero:b))
-- I leave a correct implementation of rmult as an exercise
-- for the reader.
rmult = ...
rnegate (P coeffs) = P $ map rnegate coeffs
rzero = P [0]
runit = P [1]
Related
this function works fine:
fromDigits :: [Int] -> Int
fromDigits n = sum (zipWith (\x y -> x*y) (n) (f n 0))
where
f :: [Int] -> Int -> [Int]
f n x = if (x==length n) then []
else (10^x):f n (x+1)
But if i want to change the type signature from the function, it does not work:
fromDigits :: (Num a) => [a] -> a
fromDigits n = sum (zipWith (\x y -> x*y) (n) (f n 0))
where
f :: (Num a) => [a] -> a -> [a]
f n x = if (x==length n) then []
else (10^x):f n (x+1)
Shouldn't this work too?
Almost, but not quite. The basic problem is that length has type [a]->Int. This will work:
fromDigits :: Num a => [a] -> a
fromDigits n = sum (zipWith (\x y -> x*y) (n) (f n 0))
where
f :: Num b => [b] -> Int -> [b]
f n x = if (x==length n) then []
else (10^x):f n (x+1)
As Oleg Grenrus points out, one should always be careful to check if Int might overflow. It is in fact possible for this to happen if Int is 30, 31, or 32 bits, although it is relatively unlikely. However, if that is a concern, there is a way around it, using another function:
lengthIsExactly :: Integral n => n -> [a] -> Bool
lengthIsExactly = -- I'll let you figure this one out.
-- Remember: you can't use `length` here,
-- and `genericLength` is horribly inefficient,
-- and there's a completely different way.
You can make it easier to see how the types of fromDigits and f match up using a GHC extension. Enable the extension by adding
{-# LANGUAGE ScopedTypeVariables #-}
to the very top of your source file. Then you can write
fromDigits :: forall a . Num a => [a] -> a
fromDigits n = sum (zipWith (\x y -> x*y) (n) (f n 0))
where
f :: [a] -> Int -> [a]
f n x = if (x==length n) then []
else (10^x):f n (x+1)
This way, it's clear that the list arguments all have the same type.
The problem is that you are using other functions that require even more of type a than just Num a
(^) requires "Integral a"
This works
fromDigits' :: (Num a, Integral a)=>[a] -> a
fromDigits' n = sum (zipWith (\x y -> x*y) (n) (f n 0))
where
f :: (Num a, Integral a)=>[a] -> a -> [a]
f n x = if (x==fromIntegral (length n)) then []
else (10^x):f n (x+1)
I'm trying to understand the result of
(*) . (+)
in Haskell. I know that the composition operator is just the standard composition of mathematical functions- so
(f . g) = f (g x)
But:
(*) . (+) :: (Num (a -> a), Num a) => a -> (a -> a) -> a -> a
I'm struggling to understand this type signature. I would have expected to be able to do things like:
((*) . (+)) 1 2 :: Num a => a -> a
= (* (+ 1 2))
What is the meaning of (*) . (+)'s type signature? I tried playing with it by something like (just matching up with its signature):
((*) . (+)) 1 (\x -> x + 1) 1
But that fails to compile. I'm trying to walk through the logical steps when composing these, but I'm not fully understanding how it's getting to this result (and what the result is).
I understand how you feel. I found function composition to be quite difficult to grasp at first too. What helped me grok the matter were type signatures. Consider:
(*) :: Num x => x -> x -> x
(+) :: Num y => y -> y -> y
(.) :: (b -> c) -> (a -> b) -> a -> c
Now when you write (*) . (+) it is actually the same as (.) (*) (+) (i.e. (*) is the first argument to (.) and (+) is the second argument to (.)):
(.) :: (b -> c) -> (a -> b) -> a -> c
|______| |______|
| |
(*) (+)
Hence the type signature of (*) (i.e. Num x => x -> x -> x) unifies with b -> c:
(*) :: Num x => x -> x -> x -- remember that `x -> x -> x`
| |____| -- is implicitly `x -> (x -> x)`
| |
b -> c
(.) (*) :: (a -> b) -> a -> c
| |
| |‾‾‾‾|
Num x => x x -> x
(.) (*) :: Num x => (a -> x) -> a -> x -> x
Hence the type signature of (+) (i.e. Num y => y -> y -> y) unifies with Num x => a -> x:
(+) :: Num y => y -> y -> y -- remember that `y -> y -> y`
| |____| -- is implicitly `y -> (y -> y)`
| |
Num x => a -> x
(.) (*) (+) :: Num x => a -> x -> x
| | |
| |‾‾‾‾| |‾‾‾‾|
Num y => y y -> y y -> y
(.) (*) (+) :: (Num (y -> y), Num y) => y -> (y -> y) -> y -> y
I hope that clarifies where the Num (y -> y) and Num y come from. You are left with a very weird function of the type (Num (y -> y), Num y) => y -> (y -> y) -> y -> y.
What makes it so weird is that it expects both y and y -> y to be instances of Num. It's understandable that y should be an instance of Num, but how y -> y? Making y -> y an instance of Num seems illogical. That can't be correct.
However, it makes sense when you look at what function composition actually does:
( f . g ) = \z -> f ( g z)
((*) . (+)) = \z -> (*) ((+) z)
So you have a function \z -> (*) ((+) z). Hence z must clearly be an instance of Num because (+) is applied to it. Thus the type of \z -> (*) ((+) z) is Num t => t -> ... where ... is the type of (*) ((+) z), which we will find out in a moment.
Therefore ((+) z) is of the type Num t => t -> t because it requires one more number. However, before it is applied to another number, (*) is applied to it.
Hence (*) expects ((+) z) to be an instance of Num, which is why t -> t is expected to be an instance of Num. Thus the ... is replaced by (t -> t) -> t -> t and the constraint Num (t -> t) is added, resulting in the type (Num (t -> t), Num t) => t -> (t -> t) -> t -> t.
The way you really want to combine (*) and (+) is using (.:):
(.:) :: (c -> d) -> (a -> b -> c) -> a -> b -> d
f .: g = \x y -> f (g x y)
Hence (*) .: (+) is the same as \x y -> (*) ((+) x y). Now two arguments are given to (+) ensuring that ((+) x y) is indeed just Num t => t and not Num t => t -> t.
Hence ((*) .: (+)) 2 3 5 is (*) ((+) 2 3) 5 which is (*) 5 5 which is 25, which I believe is what you want.
Note that f .: g can also be written as (f .) . g, and (.:) can also be defined as (.:) = (.) . (.). You can read more about it here:
What does (f .) . g mean in Haskell?
(*) and (+) both have the type signature Num a => a -> a -> a
Now, if you compose them, you get something funky.
(*) . (+) :: (Num (a -> a), Num a) => a -> (a -> a) -> a -> a
That's because (*) and (+) are expecting two 'arguments'.
(+) with one argument gets you a function. The . operator expects that function (the a -> a that you see).
Here's the meaning of (*) . (+)
x f y
(*) . (+) :: (Num (a -> a), Num a) => a -> (a -> a) -> a -> a
(*) . (+) maps x f y to ((x +) * f) y where f is a function from a to a that is ALSO a number.
The reason (*) expects a function is to make the types match while it expects two arguments, but that function has to be a number because (*) only works on numbers.
Really, this function makes no sense at all.
Some extensions first:
{-# LANGUAGE FlexibleContexts, FlexibleInstances, TypeSynonymInstances #-}
As the other answers show, your function is
weird :: (Num (a -> a), Num a) => a -> (a -> a) -> a -> a
weird x g = (x +) * g
But this function does have non-weird semantics.
There is a notion of difference lists. Accordingly, there is a notion of difference integers. I've seen them being used only in the dependently typed setting (e.g. here, but that's not the only case). The relevant part of the definition is
instance Enum DiffInt where
toEnum n = (n +)
fromEnum n = n 0
instance Num DiffInt where
n + m = n . m
n * m = foldr (+) id $ replicate (fromEnum n) m
This doesn't make much sense in Haskell, but can be useful with dependent types.
Now we can write
test :: DiffInt
test = toEnum 3 * toEnum 4
Or
test :: DiffInt
test = weird 3 (toEnum 4)
In both the cases fromEnum test == 12.
EDIT
It's possible to avoid the using of the TypeSynonymInstances extension:
{-# LANGUAGE FlexibleContexts, FlexibleInstances #-}
weird :: (Num (a -> a), Num a) => a -> (a -> a) -> a -> a
weird x g = (x +) * g
instance (Enum a, Num a) => Enum (a -> a) where
toEnum n = (toEnum n +)
fromEnum n = fromEnum $ n (toEnum 0)
instance (Enum a, Num a) => Num (a -> a) where
n + m = n . m
n * m = foldr (+) id $ replicate (fromEnum n) m
type DiffInt = Int -> Int
As before we can write
test' :: DiffInt
test' = weird 3 (toEnum 4)
But now we can also write
-- difference ints over difference ints
type DiffDiffInt = DiffInt -> DiffInt
test'' :: DiffDiffInt
test'' = weird (toEnum 3) (toEnum (toEnum 4))
And
main = print $ fromEnum $ fromEnum test'
prints 12.
EDIT2 Better links added.
Let:
m = (*)
a = (+)
then
(m.a) x = (m (a x)) = m (a x)
Now m expects a Num a as a parameter, on the other hand (a x) , i.e. (x +) is a unary function (a -> a) by definition of (+). I guess what happened is that GHC tries to unite these two types so that, if you have a type that is both a number and a unary function, m can take a number and a unary function and return a unary function, since they are considered the same type.
As #Syd pointed, this unification wouldn't make sense for any normal number types such as integers and floating point numbers.
There are good answers here, but let me quickly point out a few steps where you went wrong.
First, the correct definition of function composition is
(f . g) x = f (g x)
you omitted the x on the LHS. Next, you should remember that in Haskell h x y is the same as (h x) y. So, contrary to what you expected,
((*) . (+)) 1 2 = (((*) . (+)) 1) 2 = ((*) ((+) 1)) 2 = ((+) 1) * 2,
and now you see why that fails. Also,
((*) . (+)) 1 (\x -> x + 1) 1
does not work, because the constraint Num (Int -> Int) is not satisfied.
I'm trying to construct an algebraic data type that represents polynomials. Given the definition that an Integer constant is a polynomial and that if you add two polynomials or multiply two polynomials, it results in a polynomial.
I'm having a difficult time understanding how algebraic data types work in general and how I would even go about producing this. I currently have
data Poly = Const Int |
Add Poly Poly |
Mult Poly Poly
However I don't know what this even means or how to use it, I'm simply going off of examples I've seen of algebraic data types.
I've seen types like
data Tree = NullT |
Node Int Tree Tree
That makes more sense to me, and how to use it. The polynomial example seems so abstract I don't know where to start.
Edit: When I try to implement simple testing functions like:
evalPoly :: Poly -> Int
evalPoly (Const n) = n
I'm met with the error
*Polynomial> evalPoly Poly 1
<interactive>:25:10: Not in scope: data constructor ‘Poly’
*Polynomial>
Edit again: Thank you for all your suggestions and help, it's helped me produce something that's working for my purposes!
You seem to want to make an ADT for polynomials, but I'd prefer to use a Map. First some imports:
import qualified Data.Map as M
import Data.Function (on)
A polynomial is a Map from powers of x to coefficients.
newtype Poly a n = Poly {coeffMap :: M.Map n a} deriving (Show)
lift f = Poly . f . coeffMap
Let's make some simple polynomials:
zero = Poly M.empty -- none of the powers have non-zero coefficients
x = Poly $ M.singleton 1 1 -- x^1 has coefficient 1
constant 0 = zero
constant a = Poly $ M.singleton 0 a -- x^0 has coefficient a
A standard thing to do with a polynomial is evaluate it with a particular value for x.
The fold here takes the partially-calculated b and adds on the new term, a*x^n:
evalAt :: (Num a, Integral n) => a -> Poly a n -> a
evalAt x = M.foldrWithKey (\n a b -> b + a*x^n) 0 . coeffMap
If we want to use a Map function, we can lift it from Map n a to Poly n a.
I'd like to be able to map on the coefficients, but I don't want to make this an instance of Functor because it's a classic student error to apply operations like squaring, applying trigonometrical or logarithmic functions or taking square roots term by term, when in fact only a tiny few things like scalar multiplication, differentiation and integration work like this. Providing fmap encourages you to do wong things like fmap (+1) instead of (+ (constant 1)).
mapCoeffs :: (a -> b) -> Poly a n -> Poly b n
mapCoeffs f = lift (fmap f)
Maps already collect like terms automatically, but we'll want to omit terms with zero coefficients:
strikeZeros :: (Num a,Eq a) => Poly a n -> Poly a n
strikeZeros = lift $ M.filter (/= 0)
Now we can make the instances:
instance (Eq a,Num a,Ord n,Num n) => Eq (Poly a n) where
f == g = f - g == zero
instance (Eq a,Num a,Num n,Ord n) => Num (Poly a n) where
fromInteger = constant . fromInteger
signum (Poly m) | M.null m = zero
| otherwise = let (n,a) = M.findMax m in
Poly $ M.singleton n (signum a)
abs = mapCoeffs abs
negate = mapCoeffs negate
(+) = (strikeZeros.) . (Poly.) . ((M.unionWith (+)) `on` coeffMap)
(Poly m) * (Poly m') = Poly $
M.fromListWith (+) [(n+n',a*a') | (n,a)<-M.assocs m, (n',a')<-M.assocs m']
In action:
ghci> 3*x^4 + 6 + 2*x^7
Poly {coeffMap = fromList [(0,6),(4,3),(7,2)]}
Here's an alternative solution to the other one I posted.
You seem to want to make an ADT for polynomials, where I'd use a Map, but let's go with a list of terms. First some imports:
import Data.Function (on)
import Data.List (sortBy, groupBy, foldl1')
This way a polynomial is a list of terms, sorted with the highest power first, and a term is aX^n, represented by X a n
newtype Poly a n = Poly {terms :: [Term a n]} deriving (Show)
data Term a n = X {coeff :: a, power :: n} deriving (Eq,Show)
Let's make some simple polynomials:
zero = Poly []
x = Poly [X 1 1]
constant :: (Num a,Eq a,Num n) => a -> Poly a n
constant 0 = zero
constant a = Poly [X a 0]
Once we've defined the Num instance, we'll be able to make X 3 4 by writing 3*x^4.
A standard thing to do with a polynomial is evaluate it with a particular value for x.
subst :: (Num a, Integral n) => a -> Term a n -> a
subst x (X a n) = a * x ^ n
evalAt :: (Num a, Integral n) => a -> Poly a n -> a
evalAt x = sum . map (subst x) . terms
I'd like to be able to map on the coefficients, but I don't want to make this an instance of Functor because it's a classic student error to apply operations like squaring, applying trigonometrical or logarithmic functions or taking square roots term by term, when in fact only a tiny few things like scalar multiplication, differentiation and integration work like this. Providing fmap encourages you to do wong things like fmap (+1) instead of (+ (constant 1)).
mapCoeffs :: (a -> b) -> Poly a n -> Poly b n
mapCoeffs f = Poly . map f' . terms
where f' (X a n) = X (f a) n
We'll need to add and multiply terms, and collect like terms. When we collect like terms, we sort in reverse order of power and omit terms with zero coefficients.
addTerm (X a n) (X b m) | n == m = X (a+b) n
| otherwise = error "addTerm: mismatched powers"
multTerm (X a n) (X b m) = X (a*b) (n+m)
collectLikeTerms :: (Num a, Ord n, Eq a) => Poly a n -> Poly a n
collectLikeTerms = Poly . filter ((/= 0).coeff) -- no zero coeffs
. map (foldl1' addTerm) -- add the like powers
. groupBy ((==) `on` power) -- group the like powers
. sortBy (flip compare `on` power) -- sort in reverse powers
. terms
Now we can make the instances:
instance (Eq a,Num a,Ord n,Num n) => Eq (Poly a n) where
f == g = f - g == zero
instance (Eq a,Num a,Num n,Ord n) => Num (Poly a n) where
fromInteger = constant . fromInteger
signum (Poly []) = zero
signum (Poly (t:_)) = constant . signum . coeff $ t
abs = mapCoeffs abs
negate = mapCoeffs negate
(+) = (collectLikeTerms.) . (Poly.) . ((++) `on` terms)
(Poly ts) * (Poly ts') = collectLikeTerms $ Poly [multTerm t t' | t<-ts, t'<-ts']
In action:
ghci> 5*x^2 + 6*x^7 + 2
Poly {terms = [X {coeff = 6, power = 7},X {coeff = 5, power = 2},X {coeff = 2, power = 0}]}
I am trying to create a instance of Show for haskell operator or functions, but I just can't figure it out how...
I tried
instance (Show (+)) where
show (+) = "+"
but of course, it doesn't work. Does anyone know how to do it?
This might not be what you're looking for, but it might be the closest you can get: we can always turn a typeclass signature into a concrete data type.
-- built it
class Num a where
(+) :: a -> a -> a
(*) :: a -> a -> a
(-) :: a -> a -> a
negate :: a -> a
abs :: a -> a
signum :: a -> a
fromInteger :: Integer -> a
-- ours
data Num
= Plus Num Num
| Mult Num Num
| Subt Num Num
| Neg Num
| Abs Num
| Signun Num
| FromInt Integer
deriving ( Show )
instance Num Num where
(+) = Plus
(-) = Subt
(*) = Mult
negate = Neg
abs = Abs
signum = Signum
fromInteger = FromInt
And now we can use the deriving ( Show ) bit on our Num data type to see expressions.
>>> 3 + 2 :: Num
Plus (FromInt 3) (FromInt 2)
But generally there's no way much simpler than this to show Haskell functions or operators. Usually once you've gotten a value of a type like a -> b then the best thing you can do to examine it is to feed it as
instance Show a => Show (Bool -> a) where
show f = "Fun { True -> " ++ show (f True) ++ ", False -> " ++ show (f False) ++ " }"
>>> id :: Bool -> Bool
Fun { True -> True, False -> False }
>>> not
Fun { True -> False, False -> True}
So here is a nested list [[1, 2], [3, 4]]
I want to wrap it in a type called Matrix, and make it an instance of the classes Eq, Num, and Show
I have already created (add, sub, mul) operations for nested lists (matrices). How do I overload (+ - *) operators so that + maps to add, - maps to sub, and * maps to mul? So I can do this
> ma = Matrix [[1, 2], [3, 4]]
> mb = Matrix [[5, 6], [7, 8]]
> ma + mb
> ma - mb
> ma * mb
Thanks
EDIT
this is my attempt so far
> add = zipWith (zipWith (+))
> sub = zipWith (zipWith (-))
> data Matrix a = Matrix [[a]] deriving (Eq, Show)
> instance Num (Matrix a)
> where
> (+) x y = Matrix $ add x y
> (-) x y = Matrix $ sub x y
This is what I get from ghci
Couldn't match expected type `[[c0]]' with actual type `Matrix a'
In the first argument of `sub', namely `x'
In the second argument of `($)', namely `sub x y'
In the expression: Matrix $ sub x y
EDIT #2
The last thing I need to figure out right now is
how to I print
1 2
3 4
Instead of Matrix [[1,2],[3,4]]
Are you having a problem with defining a Num instance for your type? Try this code:
data Matrix a = Matrix [[a]]
deriving (Eq)
plus_mat :: Num a => [[a]] -> [[a]] -> [[a]]
plus_mat = zipWith (zipWith (+))
instance Num a => Num (Matrix a)
where
(Matrix a) + (Matrix b) = Matrix $ plus_mat a b
(-) = undefined
(*) = undefined
negate = undefined
abs = undefined
signum = undefined
fromInteger = undefined
Testing:
*Main> Matrix [[1,2],[3,4]] + Matrix [[5,6],[7,8]]
Matrix [[6,8],[10,12]]
Definitions of the remaining class methods are left as exercise.
And here's a Show instance for Matrix:
import Data.List
instance Show a => Show (Matrix a)
where
show (Matrix a) = intercalate "\n" $ map (intercalate " " . map show) a
Testing:
*Main Data.List> Matrix [[1,2,3], [4,5,6]]
1 2 3
4 5 6
If you inspect the type of your add and sub, you will see the issue.
ghci> :t add
add :: Num a => [[a]] -> [[a]] -> [[a]]
ghci> :t sub
sub :: Num a => [[a]] -> [[a]] -> [[a]]
Mikhail's suggestion was to essentially unwrap the 2D list and rewrap it in the Num instance methods. Another way to do this is to modify your add and sub methods to work on Matrices instead. Here I use a "lifting" approach, where I write combinators to "lift" a function from one type to another.
-- unwraps the 2d list from a matrix
unMatrix :: Matrix a -> [[a]]
unMatrix (Matrix m) = m
-- lifts a 2d list operation to be a Matrix operation
liftMatrixOp :: ([[a]] -> [[a]] -> [[a]]) -> Matrix a -> Matrix a -> Matrix a
liftMatrixOp f x y = Matrix $ f (unMatrix x) (unMatrix y)
-- lifts a regular operation to be a 2d list operation
lift2dOp :: (a -> a -> a) -> [[a]] -> [[a]] -> [[a]]
lift2dOp f = zipWith (zipWith f)
With these combinators, defining add and sub is simply a matter of lifting appropriately.
add, sub :: Num a => Matrix a -> Matrix a -> Matrix a
add = liftMatrixOp add2D
sub = liftMatrixOp sub2D
add2D, sub2D :: Num a => [[a]] -> [[a]] -> [[a]]
add2D = lift2dOp (+)
sub2D = lift2dOp (-)
Now that we have functions that work on Matrices, the Num instance is simple
instance (Num a) => Num (Matrix a) where
(+) = add
(-) = sub
..etc..
Of course we could have combined lift2dOp and liftMatrixOp into one convenience function:
-- lifts a regular operation to be a Matrix operation
liftMatrixOp' :: (a -> a -> a) -> Matrix a -> Matrix a -> Matrix a
liftMatrixOp' = liftMatrixOp . lift2dOp
instance (Num a) => Num (Matrix a) where
(+) = liftMatrixOp' (+)
(-) = liftMatrixOp' (-)
(*) = liftMatrixOp' (*)
..etc..
Now you try: define liftMatrix :: (a -> a) -> Matrix a -> Matrix a, a lifting function for unary functions. Now use that to define negate, abs, and signum. The docs suggest that abs x * signum x should always be equivalent to x. See if this is true for our implementation.
ghci> quickCheck (\xs -> let m = Matrix xs in abs m * signum m == m)
+++ OK, passed 100 tests.
In fact, if you write liftMatrix with the more lenient type signature, it can be used to define a Functor instance for Matrices.
liftMatrix :: (a -> b) -> Matrix a -> Matrix b
instance Functor (Matrix a) where
fmap = liftMatrix
Now think about how you could implement fromInteger. Implementing this allows you to do stuff like this in ghci:
ghci> Matrix [[1,2],[3,4]] + 1
Matrix [[2,3],[4,5]]
That's how it works the way I implemented it, anyways. Remember that any numeric literal n in Haskell code is actually transformed into fromInteger n, which is why this works.
I think that's enough fun for now, but if you need more exercises, try getting comfortable with this Arbitrary instance of Matrices:
instance Arbitrary a => Arbitrary (Matrix a) where
arbitrary = liftM Matrix arbitrary