在Idris中划分向量:为什么可以';t0和m+;n要统一吗?

在Idris中划分向量:为什么可以';t0和m+;n要统一吗?,idris,unify,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::y

我想把一个向量分成两个新向量

我们无法知道各个向量的长度,但得到的向量之和必须等于参数。我尝试捕获此属性,如下所示:

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报告(指向“partition 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明显。if如何让Idris相信这一点呢?

记住这是一种语法操作,在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
,对于
a
上的所有布尔谓词
p
,给定长度向量
加上mn
,我可以生成一对由长度向量
m
和长度向量
n
换句话说,要调用你的函数,我需要提前知道向量中有多少元素满足谓词,因为我需要在调用站点提供
m
n

我认为您真正想要的是一个
分区
函数,给定一个长度为
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'))))
完整的实现如下所示:

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'))))
这有点多,让我们来解剖一下。 为了实现该功能,我们首先在输入向量上进行模式匹配:

partition p [] = (Z ** (Z ** ([], [], refl)))
请注意,唯一可能的输出是右侧的内容,否则我们无法构建
refl
。我们知道
n
Z
,这是因为
n
Vect
的构造函数
Nil
的索引相统一

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

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(sm')
不能与
S(plus m')
统一。还记得我说过统一不能达到我们可以证明的平等吗?库函数
plusSuccRightSucc
满足我们的需要,不过:

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

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

plusSuccRightSucc : (left : Nat) ->
                    (right : Nat) ->
                    S (plus left right) = plus left (S right)
sym : (l = r) -> r = l
上述函数中缺少的一点是,该函数实际上对
Vect
进行了分区。我们可以通过使结果向量由依赖的元素对和每个元素满足
p
非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'))))
如果你想变得更疯狂,你也可以让每个元素证明它是输入向量的一个元素,并且输入向量的所有元素在输出中只出现一次,以此类推。依赖类型为您提供了做这些事情的工具,但在每种情况下都值得考虑复杂性权衡