请记住,统一是一种句法操作,在像 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
但这不会让统一者知道这个事实,因为这会使类型检查无法确定。
回到你原来的问题:如果我将你的分区类型翻译成英文,它会说“对于所有自然数m
和n
,对于所有布尔谓词p
over a
,给定一个长度向量plus m n
,我可以产生一个由长度向量组成的对m
和长度向量n
"。换句话说,要调用您的函数,我需要提前知道向量中有多少元素满足谓词,因为我需要在调用站点提供m
和!n
我认为你真正想要的是一个partition
函数,给定一个长度为的向量n
,返回一对长度加起来为 的向量n
。我们可以使用依赖对来表达这一点,这是存在量化的类型论版本。“一对长度加起来的向量”的翻译n
是“存在一些具有这些长度的m
和m'
和向量,使得和的总和m
是m'
我的输入n
。”
这种类型看起来像这样:
partition : (a -> Bool) -> Vect n a -> (m ** (m' ** (Vect m a, Vect m' a, m+m'=n)))
完整的实现如下所示:
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'))))
这有点拗口,所以让我们剖析一下。为了实现该功能,我们首先对输入 Vect 进行模式匹配:
partition p [] = (Z ** (Z ** ([], [], refl)))
请注意,唯一可能的输出是右侧的内容,否则我们无法构造refl
. 我们知道这n
是Z
由于与 的构造函数的n
索引的统一。Nil
Vect
在递归的情况下,我们检查向量的第一个元素。在这里,我使用with
规则是因为它是可读的,但我们可以if
在右侧使用 an 而不是p x
在左侧匹配。
partition p (x :: xs) with (p x, partition p xs)
在这种True
情况下,我们将元素添加到第一个子向量。因为plus
reduce 在它的第一个参数上,所以我们可以构造等式证明,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
上述函数中缺少的一件事是该函数实际上将Vect
. 我们可以通过使结果向量由依赖的元素对和每个元素满足p
或的证据来添加这一点not p
:
partition' : (p : a -> Bool) ->
(xs : Vect n a) ->
(m ** (m' ** (Vect m (y : a ** so (p y)),
Vect m' (y : a ** so (not (p y))),
m+m'=n)))
partition' p [] = (0 ** (0 ** ([], [], refl)))
partition' p (x :: xs) with (choose (p x), partition' p xs)
partition' p (x :: xs) | (Left oh, (m ** (m' ** (ys, ns, refl)))) =
(S m ** (m' ** ((x ** oh)::ys, ns, refl)))
partition' p (x :: xs) | (Right oh, (m ** (m' ** (ys, ns, refl)))) =
(m ** (S m' ** (ys, (x ** oh)::ns, sym (plusSuccRightSucc m m'))))
如果你想更疯狂,你还可以让每个元素证明它是输入向量的一个元素,并且输入向量的所有元素都在输出中恰好出现一次,依此类推。依赖类型为您提供了执行这些操作的工具,但值得考虑每种情况下的复杂性权衡。