本书Type Driven Development with Idris介绍了这个练习:
定义适合签名的可能方法:
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的Vector,并且需要一个大小为N * 2的Vector,那么将它附加到自身似乎是合理的。
我做错了什么?
答案 0 :(得分:12)
将类型签名更改为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 + n
和mult n 2
是平等的,但它们的正常形式并不相同。 (mult n 2
是解析类型类后n * 2
减少的内容。)
你可以看到mult
的定义如下:
*kevinmeredith> :printdef mult
mult : Nat -> Nat -> Nat
mult 0 right = 0
mult (S left) right = plus right (mult left right)
它通过第一个参数的模式匹配来工作。由于two
类型签名中的第一个参数是n
,mult
根本无法减少。multCommutative
。 *kevinmeredith> :t multCommutative
multCommutative : (left : Nat) ->
(right : Nat) -> left * right = right * left
会帮助我们解决问题:
rewrite
我们应用这种平等的最佳工具是two'
,就像我对:t replace
的定义一样。 (如果你想看看如何做到这一点,请在REPL上运行rewrite foo in bar
)在foo
构造中,a = b
类型bar
和{{1}具有外部表达式的类型,但所有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)