(xs : Vect n elem) -> Vect (n * 2) 元素

(xs : Vect n elem) -> Vect (n * 2) elem

本书 Type Driven Development with Idris 介绍了这个练习:

Define a possible method that fits the signature:

two : (xs : Vect n elem) -> Vect (n * 2) elem

我试过了:

two : (xs : Vect n elem) -> Vect (n * 2) elem
two xs = xs ++ xs

但是我得到以下错误:

*One> :r
Type checking ./One.idr
One.idr:9:5:When checking right hand side of two:
Type mismatch between
        Vect (n + n) elem (Type of xs ++ xs)
and
        Vect (mult n 2) elem (Expected type)

Specifically:
        Type mismatch between
                plus n n
        and
                mult n 2
Holes: Hw1.two

如果我有一个大小为 N 的向量,并且需要一个大小为 N*2 的向量,那么将它附加到自身似乎是合理的。

我做错了什么?

简答

将类型签名更改为 two : (xs : Vect n elem) -> Vect (n + n) elem

如果你真的需要那样

要达到 Vect (n * 2) elem 有点复杂。这里:

two' : Vect n elem -> Vect (n * 2) elem
two' {n} xs = rewrite multCommutative n 2 in rewrite plusZeroRightNeutral n in xs ++ xs

您收到该错误消息的原因是类型检查中的相等性是还原为正常形式后的相等性。 n + nmult n 2 相等,但它们的正常形式不同。 (mult n 2n * 2 解析类型类后的结果。)

你可以这样看mult的定义:

*kevinmeredith> :printdef mult
mult : Nat -> Nat -> Nat
mult 0 right = 0
mult (S left) right = plus right (mult left right)

它通过对第一个参数进行模式匹配来工作。由于 two 类型签名中的第一个参数是 n,因此根本无法减少 multmultCommutative 将帮助我们翻转它:

*kevinmeredith> :t multCommutative 
multCommutative : (left : Nat) ->
                  (right : Nat) -> left * right = right * left

我们应用平等的最佳工具是 rewrite,就像我对 two' 的定义一样。 (运行 :t replace 在 REPL 中,如果你想看看如何以困难的方式做到这一点)在 rewrite foo in bar 结构中,fooa = b 类型的东西bar 具有外部表达式的类型,但所有 a 都被 b 替换。在我上面的two'中,我首先用它把Vect (n * 2)改成了Vect (2 * n)。这让 mult 减少。如果我们查看上面的 mult,并将其应用于 2,即 S (S Z)n,您会得到 plus n (mult (S Z) n,然后是 plus n (plus n (mult Z n)),然后然后 plus n (plus n Z)。你不必自己计算减少量,你可以只应用重写并在末尾打一个洞:

two' : Vect n elem -> Vect (n * 2) elem
two' {n} xs = rewrite multCommutative n 2 in ?aaa

然后问伊德里斯:

*kevinmeredith> :t aaa
  elem : Type
  n : Nat
  xs : Vect n elem
  _rewrite_rule : plus n (plus n 0) = mult n 2
--------------------------------------
aaa : Vect (plus n (plus n 0)) elem

plus n Z 不会归约,因为 plus 是由第一个参数的递归定义的,就像 mult 一样。 plusZeroRightNeutral 为您提供所需的平等:

*kevinmeredith> :t plusZeroRightNeutral 
plusZeroRightNeutral : (left : Nat) -> left + 0 = left

我再次使用与 rewrite 相同的技术。

:search 可让您在图书馆中搜索特定类型的居民。你会经常发现有人已经为你完成了证明事情的工作。

*kevinmeredith> :s (n : Nat) -> n + 0 = n
= Prelude.Nat.multOneLeftNeutral : (right : Nat) ->
                                   fromInteger 1 * right = right


= Prelude.Nat.plusZeroRightNeutral : (left : Nat) ->
                                     left + fromInteger 0 = left


*kevinmeredith> :s (n, m : Nat) -> n * m = m * n
= Prelude.Nat.multCommutative : (left : Nat) ->
                                (right : Nat) -> left * right = right * left

(此答案适用于 Idris 版本 0.9.20.1)