Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
具有算子重载的Haskell素数有限域(Z/pZ)_Haskell_Operator Overloading - Fatal编程技术网

具有算子重载的Haskell素数有限域(Z/pZ)

具有算子重载的Haskell素数有限域(Z/pZ),haskell,operator-overloading,Haskell,Operator Overloading,我现在有一些代码,它创建了一个n阶素数域,包含计算加法、乘法及其逆的所有必要函数。它工作得很好,但我确实希望能够重载+、-、*、/、和^的整数和Num中缀函数,但我不知道如何做到这一点。以下是我当前的代码: import Data.Maybe data FiniteField = FiniteField {add::(FieldElement -> FieldElement -> FieldElement), addinv::(FieldElement

我现在有一些代码,它创建了一个n阶素数域,包含计算加法、乘法及其逆的所有必要函数。它工作得很好,但我确实希望能够重载+、-、*、/、和^的整数和Num中缀函数,但我不知道如何做到这一点。以下是我当前的代码:

import Data.Maybe

data FiniteField = FiniteField {add::(FieldElement -> FieldElement -> FieldElement),
              addinv::(FieldElement -> FieldElement),
              mul::(FieldElement -> FieldElement -> FieldElement),
              mulinv::(FieldElement -> FieldElement),
              new::(Integer -> FieldElement)
              }

newtype FieldElement = FieldElement Integer deriving (Eq, Show, Read)

toInt :: FieldElement -> Integer
toInt (FieldElement x) = x

gcdExt :: Integer -> Integer -> (Integer, Integer, Integer)
gcdExt a 0 = (1, 0, a)
gcdExt a b = (t, s - q * t, g)
    where (q, r) = quotRem a b
          (s, t, g) = gcdExt b r

modMulInv :: Integer -> Integer -> Integer
modMulInv a n = i
    where (i, _, _) = gcdExt a n

isPrimeLoop :: Integer -> Integer -> Bool
isPrimeLoop n i
    | i == n = True
    | i == 2 = (mod n i /= 0) && isPrimeLoop n (i+1)
    | otherwise = (mod n i /= 0) && isPrimeLoop n (i+2)

isPrime :: Integer -> Bool
isPrime n = isPrimeLoop n 2

newFiniteField :: Integer -> Maybe FiniteField
newFiniteField n
    | not (isPrime n) = Nothing
    | otherwise = Just (FiniteField add addinv mul mulinv new)
    where
        add = (\x y -> FieldElement (mod (toInt x + toInt y) n) )
        addinv = (\x -> FieldElement (mod (n - toInt x) n) )
        mul = (\x y -> FieldElement (mod (toInt x * toInt y) n) )
        mulinv = (\x -> FieldElement (mod (modMulInv (toInt x) n) n) )
        new = (\x -> FieldElement x)

您无法有效地将
Num
用于此设计。关于类型类的重要一点是,分派是按类型而不是按值完成的。
FieldElement
Num
实例无法知道它属于什么
FiniteField
,因此它的操作不能取决于您在哪个字段中操作

您可以从以下几个方面入手使用
Num

第一种方法是使
FieldElement
成为一种表达式类型,它使用
Num
实例建立表达式树,然后可以在特定的
FiniteField
中进行计算。这具有使用非常简单的技术的优点。当计算变得复杂时,它的缺点是对内存和性能非常不利

第二种是遵循类似于
Data.Fixed
的模式。您可以将
FiniteField
更改为一个类,并在一些表示各种特定字段的空类型上实现它,例如名称为
F17
。然后使用类型参数对
FieldElement
进行参数化,该参数用于标记它们属于哪个
FiniteField
。最后,
FiniteElement
Num
实例要求其参数具有一个
FiniteField
实例,该实例在其实现中使用。这种方法的优点是非常好用。缺点是需要为每个要处理的字段提供样板文件
FiniteField
实例


第三个选项与上面的选项非常类似,但将自定义的类似于
F17
的数据类型替换为某种类型级别的自然类型。(手动或从
-XDataKinds
中)。然后可以根据类型级别实现
Num
实例。这里的优点是,您摆脱了前面方法中的所有样板实例。缺点是它不要求类型级参数是素数,如果它不是素数,则有几次计算都是错误的。

您必须解决的主要问题是,不允许在不同的顺序下对
FiniteField
的值进行加法/乘法/etc。从类型系统的角度来看,解决方案非常简单:为不同的类型提供不同顺序的值

newtype FieldElem (n :: Nat) = FieldElem Integer
Nat
是一种(来自GHC.TypeLits模块),它的居民是类型级别的数字文字,如
1
2
3

现在,您有不同的类型:

FieldElem 7     -- the type of an element of a finite field of order 7
FieldElem 11    -- the type of an element of a finite field of order 11
因此,如果尝试添加两个不同类型的值,则会出现编译错误

> (x :: FieldElem 7) + (y :: FieldElem 11)
Error!  You can only use + on two things of the same type!
> (x :: FieldElem 7) + (y :: FieldElem 7)
-- result: something of type FieldElem 7
现在您可以实现
Num
实例:

instance Num (FieldElem n) where
   (+) = ...
   (*) = ...
这里的一个问题是,
(+)
需要知道顺序是什么,唯一的信息是
FieldElem
的类型。为了解决这个问题,我们需要
n
成为
KnownNat
typeclass(也来自GHC.TypeLits)的一个实例,它允许我们在运行时获取其整数值作为一个值:

natVal :: KnownNat n => Proxy n -> Integer
所以

因此我们的最终设计:(需要
ScopedTypeVariables
让我们使用
n
类型变量)

等等

您可以使用智能构造函数将
Integer
s引入
FieldElem

mkFieldElem :: forall n. KnownNat n => Integer -> Maybe (FieldElem n)
mkFieldElem x | isPrime n = Just (FieldElem (mod x n))
              | otherwise = Nothing
  where
    n = natVal (Proxy :: Proxy n)
很好的一点是,您可以使用Haskell的类型推断来指定所需的顺序:

> mkFieldElem 10 :: Maybe (FieldElem 23)
Just (FieldElem 10)     -- :: Maybe (FieldElem 23)
而不是手动将其作为参数传递!:)

例如,通过使用智能构造函数(并隐藏实际的构造函数),您可以确保用户从未拥有任何类型为
FieldElem 8
的值,因此您不必担心将坏顺序的字段添加到一起

请注意,不幸的是,
frominger::KnownNat n=>Integer->FieldElem n
必须是部分的。它必须拒绝坏命令。但是在base中有大量实例部分实现了
fromInteger
:|但是,
fromInteger
Num
中无论如何都是一个坏主意,
Num
是一个坏类型类,所以这是
Num
的错误:)


EDIT有一种可能的方法可以使
fromInteger
不部分/全部:我们可以创建
Prime
类型类,并且只有
Nat
参数为Prime的实例:

class KnownNat n => Prime (n :: Nat)
然后你可以做:

mkFieldElem :: Prime n => Integer -> FieldElem n
mkFieldElem x = FieldElem (mod x n)
  where
    n = natVal (Proxy :: Proxy n)
现在如果你有:

instance Prime n => Num (FieldElem n) where
  ...
  fromInteger = mkFieldElem
fromInteger
将是一个total函数,因为只有prime-order字段才有实例

但是,为了使其工作,您需要以GHC能够理解的方式获取
Prime
的实例。理论上,这可以通过使用GHC类型检查器扩展来实现——您可以编写自己的类型检查器扩展,这样
n
就会得到一个
Prime
实例,如果它在编译时是Prime。然而,这还没有完成。。。您可以做的下一件最好的事情是提供运行时的素数证明:

witPrime :: forall n.KnownNat n => Proxy n -> Maybe (Dict (Prime n))
witPrime p | isPrime (natVal p) = Just (unsafeCoerce (Dict :: Dict (KnownNat n))
           | otherwise          = Nothing
这是使用库中的
Dict
,这是在运行时生成typeclass实例的一种方法。如果在类型为
Dict c
的值的
Dict
构造函数上进行模式匹配,则实例
c
在该case语句中为“范围内”

那么,在我们的情况下,我们可以做到:

case witPrime (Proxy :: Proxy 11) of
  Just Dict -> ... -- in this branch, `Prime 11` is an instance we can use
  Nothing   -> ... -- here, it isn't
或者我们可以在GHCi中运行它:

> let x = mkFieldElem 6 :: FieldElem 11
Error: No instance for (Prime 11)
> case witPrime (Proxy :: Proxy 11) of
    Just Dict -> let x = mkFieldElem 6 :: FieldElem 11  -- okay, because of Dict constructor match
                 in  print x
FieldElem 6    -- :: FieldElem 11

有没有办法改变设计,让我可以使用
Num
?@Izakweiss有很多选项-我在过去12分钟左右的编辑中介绍了其中的一些选项。
case witPrime (Proxy :: Proxy 11) of
  Just Dict -> ... -- in this branch, `Prime 11` is an instance we can use
  Nothing   -> ... -- here, it isn't
> let x = mkFieldElem 6 :: FieldElem 11
Error: No instance for (Prime 11)
> case witPrime (Proxy :: Proxy 11) of
    Just Dict -> let x = mkFieldElem 6 :: FieldElem 11  -- okay, because of Dict constructor match
                 in  print x
FieldElem 6    -- :: FieldElem 11