Another way to do this is to define a Field
typeclass:
class Field a where
rplus :: a -> a -> a
rmult :: a -> a -> a
rnegate :: a -> a
rinverse :: a -> a
runit :: a
rzero :: a
For each field, define a type that represents the underlying set of the field, then define an appropriate instance for that type. You are responsible for ensuring that your implementation obeys the various field laws (multiplication distributes over addition, any element times its inverse is 1, etc).
See the last example for how to define a Field
instance for a polynomial over a field.
Examples
Integers mod 7
-- Haskell doesn't have dependent types yet, so use the
-- next best thing: a smart constructor
newtype Z7 = Z7 Integer
makeZ7 :: Integer -> Z7
makeZ7 x = Z7 $ (x `mod` 7)
instance Field Z7 where
rplus (Z7 a) (Z7 b) = makeZ7 (a + b)
rmult (Z7 a) (Z7 b) = makeZ7 (a * b)
rzero = makeZ7 0
runit = makeZ7 1
rnegate (Z7 a) = makeZ7 (7 - a)
-- Necessarily partial, since 0 doesn't have an inverse
rinverse (Z7 a) | a == 1 = makeZ7 1
| a == 2 = makeZ7 4
| a == 3 = makeZ7 5
| a == 4 = makeZ7 2
| a == 5 = makeZ7 3
| a == 6 = makeZ7 6
Rationals
This one is easier because Integral a => Ratio a
is already an instance of Num
, so we get most of the implementation for free.
import Data.Ratio
instance Integeral a => Field (Ratio a) where
rplus = (+)
rmult = (*)
rnegate = negate
rinverse q = denominator q % numerator q
rzero = 0
runit = 1
Polynomials over a field
The coefficients of a polynomial can be anything you want.
data Poly f = Poly [(f, Int)]
However, polynomials only form a field if the coefficients themselves are from a field, so this is where you declare that constraint.
-- A type Poly f is a field if f is a field.
-- Fields: Poly Z7, Integral a => Poly (Ratio a)
-- Not a field: Poly Integer
instance Field f => Field (Poly f) where
runit = Poly [(runit, 0)] -- Not Poly [(1,0)]
rzero = Poly [(rzero, 0)] -- Not Poly [(0,0)]
-- Not Poly [(-c, e) | (c,e) <- p]
rnegate (Poly p) = Poly [(rnegate c, e) | (c,e) <- p]
-- Left as an exercise for the reader
-- Remember to use runit instead of 1, rzero instead of 0
-- and rnegate instead of - where appropriate.
rinverse p = ...
rplus p q = ...
rmult p q = ...
Some very quick examples that don't require me to finish implementing the Poly
instance. (You can derive Show
for all the types involved.)
> runit :: Z7
Z7 1
> runit :: Rational -- type Rational = Ratio Integer
1 % 1
> runit :: Poly Z7
Poly [(Z7 1,0)]
> runit :: Poly Rational
Poly [(1 % 1,0)]