按类和/或数据从代数中声明一个字段?

时间:2016-08-24 15:09:14

标签: haskell algebra

我想在Haskell中建模field。字段是一组元素加上两个操作,可以看作乘法和加法。我进行如下:

data Field a = Field [a] (a -> a -> a) (a -> a -> a)

elements :: Field a -> [a]
elements (Field els _ _) = els

add :: Field a -> a -> a -> a
add (Field _ addop _) = addop

mul :: Field a -> a -> a -> a
mul (Field _ _ mulop) = mulop

但是,我现在想在字段上建模多项式,例如

data Poly = Poly f [(a, Int)]

其中f应为字段,a应为字段的类型。列表[(a, Int)]表示多项式,例如

[(4, 2), (1, 1)] = 4 * x^2 + 1 * x^1

但我想不出如何在Haskell中实现这一目标。我想也许某种程度上我可以将Field声明为一个类,然后以某种方式将Poly限制为仅使用Fields,但据我所知,我不能约束数据构造函数。

1 个答案:

答案 0 :(得分:1)

另一种方法是定义Field类型类:

class Field a where
    rplus :: a -> a -> a
    rmult :: a -> a -> a
    rnegate :: a -> a
    rinverse :: a -> a
    runit :: a
    rzero :: a

对于每个字段,定义表示字段的基础集的类型,然后为该类型定义适当的实例。您有责任确保您的实施符合各种现场法律(乘法分配加法,任何元素乘以其逆数为1等)。

有关如何为字段上的多项式定义Field实例,请参阅最后一个示例。

实施例

整数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

有理数

这个更容易,因为Integral a => Ratio a已经是Num的一个实例,因此我们可以免费获得大部分实现。

import Data.Ratio
instance Integeral a => Field (Ratio a) where
    rplus = (+)
    rmult = (*)
    rnegate = negate
    rinverse q = denominator q % numerator q
    rzero = 0
    runit = 1

场上的多项式

多项式的系数可以是任何你想要的。

data Poly f = Poly [(f, Int)]

但是,如果系数本身来自一个字段,则多项式只会形成一个字段,因此这是您声明该约束的地方。

-- 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 = ...

一些非常快速的示例,不要求我完成Poly实例的实现。 (您可以为所涉及的所有类型派生Show。)

> 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)]