在Idris中对矢量进行分区:为什么&0; t 0和m + n可以统一?

时间:2014-05-05 16:15:32

标签: idris unify

我想在两个新的向量中分割一个向量。

我们无法知道单个向量的长度是多少,但结果向量的总和必须等于参数。我尝试捕获此属性如下:

partition : (a -> Bool) -> Vect (m+n) a -> (Vect m a, Vect n a)
partition p [] = ([], [])
partition p (x::xs)
  = let (ys,zs) = partition p xs
  in case p xs of
    True  => (x::ys, zs)
    False => (ys, zs)

但Idris报道(指向“分区p []”)在详细阐述Main.partition的左侧时:

Can't unify
        Vect 0 a
with
        Vect (m + n) a

Specifically:
        Can't unify
                0
        with
                plus m n

为什么会这样?

对我来说,似乎很明显,如果“0 = m + n”而不是m = n = 0.如果说服伊德里斯这个怎么样呢?

1 个答案:

答案 0 :(得分:10)

请记住,unification是一种语法操作,在像Idris这样的语言中,根据模式匹配可以直接缩减。它不知道我们可以证明的所有事实!

我们当然可以在Idris中证明,如果n + m = 0则m = 0且n = 0:

sumZero : (n, m : Nat) -> plus n m = Z -> (n=Z, m=Z)
sumZero Z m prf = (refl, prf)
sumZero (S k) m refl impossible

但这并不能使统一者知道这个事实,因为这会使类型检查变得不可判定。

回到原来的问题:如果我将您的分区类型翻译成英语,则表示“对于所有自然数mn,对于所有布尔谓词p而不是{ {1}},给定长度为a的向量,我可以生成一对由长度为plus m n的向量和长度为m的向量组成的对。换句话说,要调用你的函数,我需要提前知道向量中有多少元素满足谓词,因为我需要在调用站点提供nm

我认为你真正想要的是n函数,给定长度为partition的向量,返回一对长度加起来为n的向量。我们可以使用依赖对来表达这一点,这是存在量化的类型理论版本。 “长度加起来为n”的“一对向量”的翻译是“存在一些nm以及具有这些长度的向量,使得m'的总和并m是我的输入m'。“

此类型如下所示:

n

,完整的实现如下:

partition : (a -> Bool) -> Vect n a -> (m ** (m' ** (Vect m a, Vect m' a, m+m'=n)))

这有点拗口,所以让我们剖析它。 为了实现这个功能,我们首先在输入Vect上进行模式匹配:

partition : (a -> Bool) -> Vect n a -> (m ** (m' ** (Vect m a, Vect m' a, m+m'=n)))
partition p [] = (Z ** (Z ** ([], [], refl)))
partition p (x :: xs) with (p x, partition p xs)
  | (True, (m ** (m' ** (ys, ns, refl)))) = (S m ** (m' ** (x::ys, ns, refl)))
  | (False, (m ** (m' ** (ys, ns, refl)))) =
      (m ** (S m' ** (ys, x::ns, sym (plusSuccRightSucc m m'))))

请注意,唯一可能的输出是右侧的输出,否则我们无法构造partition p [] = (Z ** (Z ** ([], [], refl))) 。由于refln的构造函数Z的索引统一,我们知道nNil

在递归的情况下,我们检查向量的第一个元素。在这里,我使用Vect规则,因为它是可读的,但我们可以在右侧使用with,而不是在左侧的if上匹配。

p x

partition p (x :: xs) with (p x, partition p xs) 的情况下,我们将元素添加到第一个子向量。因为True减少了它的第一个参数,我们可以使用plus构造相等证明,因为加法变得恰到好处:

refl

| (True, (m ** (m' ** (ys, ns, refl)))) = (S m ** (m' ** (x::ys, ns, refl))) 案例中,我们需要做更多工作,因为False无法与plus m (S m')统一。还记得我说过统一无法获得我们可以证明的平等吗?尽管如此,库函数S (plus m m')可以满足我们的需求:

plusSuccRightSucc

作为参考, | (False, (m ** (m' ** (ys, ns, refl)))) = (m ** (S m' ** (ys, x::ns, sym (plusSuccRightSucc m m')))) 的类型为:

plusSuccRightSucc

plusSuccRightSucc : (left : Nat) -> (right : Nat) -> S (plus left right) = plus left (S right) 的类型为:

sym

上述函数中缺少的一件事是函数实际上对sym : (l = r) -> r = l 进行了分区。我们可以通过使结果向量由依赖的元素对和每个元素满足Vectp的证据来添加:

not p

如果你想变得更加疯狂,你也可以让每个元素证明它是输入向量的一个元素,并且输入向量的所有元素都在输出中只有一次,依此类推。依赖类型为您提供了执行这些操作的工具,但值得考虑每种情况下的复杂性权衡。