我假设,不可能在haskell中添加两个类型级自然数。这是真的?
假设自然数的定义如下:
class HNat a
data HZero
instance HNat HZero
data HSucc n
instance (HNat n) => HNat (HSucc n)
以类似于:
的方式定义HAdd是否合适class (HNat n1, HNat n2, HNat ne) => HAdd n1 n2 ne | n1 n2 -> ne
instance HAdd HZero HZero HZero
instance (HNat x) => HAdd HZero x x
instance (HNat n1
,HNat x) => HAdd (HSucc n1) x (HAdd n1 (HSucc x) (???))
答案 0 :(得分:20)
您无需添加HZero
和HZero
的情况。第二种情况已经涵盖了这一点。想想你如何通过对第一个论点的归纳来在术语水平上添加Peano自然:
data Nat = Zero | Succ Nat
add :: Nat -> Nat -> Nat
add Zero y = y
add (Succ x) y = Succ (add x y)
现在,如果您正在使用函数依赖项,那么您正在编写逻辑程序。因此,不要在右侧进行递归调用,而是在左侧为递归调用的结果添加约束:
class (HNat x, HNat y, HNat r) => HAdd x y r | x y -> r
instance (HNat y) => HAdd HZero y y
instance (HAdd x y r) => HAdd (HSucc x) y (HSucc r)
第二个实例中不需要HNat
约束。它们被类中的超类约束所暗示。
现在,我认为进行这种类型级编程的最好方法是使用DataKinds
和TypeFamilies
。您在术语级别上定义:
data Nat = Zero | Succ Nat
然后,您可以将Nat
不仅用作类型,还可以用作种类。然后,您可以在两个自然数上定义类型系列,如下所示:
type family Add (x :: Nat) (y :: Nat) :: Nat
type instance Add Zero y = y
type instance Add (Succ x) y = Succ (Add x y)
这更接近添加的术语级定义。此外,使用“提升”类Nat
可以使您不必定义HNat
等类。
答案 1 :(得分:3)
有可能。查看包type-level-natural-number
和`type-level-natural-number-operations
。两者都有点旧,但易于使用,后者定义了Plus
类型系列。
无论如何,我会将你的最后一行更改为这样的东西(我没有测试是否编译)。
instance (HNat n1, HNat x, HAdd n1 x y) => HAdd (HSucc n1) x (HAdd n1 x (HSucc y))
基本上,你所做的是以归纳方式定义加法,而附加约束HAdd n1 x y
增加了必要的归纳案例。