Agda中固定长度向量函数中的隐式长度参数

时间:2012-10-19 21:40:26

标签: haskell functional-programming agda dependent-type

我写了一个Agda函数prefixApp,它将Vector-Function应用于向量的前缀:

split : {A : Set}{m n : Nat} -> Vec A (n + m) -> (Vec A n) * (Vec A m) 
split {_} {_} {zero}  xs        = ( [] , xs )
split {_} {_} {suc _} (x :: xs) with split xs 
... | ( ys , zs ) = ( (x :: ys) , zs )

prefixApp : {A : Set}{n m k : Nat} -> (Vec A n -> Vec A m) -> Vec A (n + k) -> Vec A (m + k)
prefixApp f xs with split xs
... | ( ys , zs ) = f ys ++ zs

我喜欢这样的事实:prefixApp可以在没有明确提供长度参数的情况下使用,例如。

gate : Vec Bool 4 -> Vec Bool 3
gate = prefixApp xorV

(其中xorV : Vec Bool 2 -> Vec Bool 1是Vector-Xor-Function)

不幸的是,我不知道如何编写postfixApp - 函数,可以在不明确提供长度参数的情况下使用它。到目前为止,我的函数定义如下所示:

postfixApp : {A : Set}{n m k : Nat} -> (Vec A n -> Vec A m) -> Vec A (k + n) -> Vec A (k + m)
postfixApp {_} {_} {_} {k} f xs with split {_} {_} {k} xs
... | ( ys , zs ) = ys ++ (f zs)

然而,似乎postfixApp总是需要一个长度参数。 E.g。

gate : Vec Bool 4 -> Vec Bool 3
gate = postfixApp {k = 2} xorV

有谁知道,如何消除这种不对称性,即如何编写一个没有显式长度参数的函数postfixApp。我想,我需要另一个split - 函数?

2 个答案:

答案 0 :(得分:8)

使用prefixApp,您有

prefixApp : {A : Set}{n m k : Nat} -> (Vec A n -> Vec A m) -> Vec A (n + k) -> Vec A (m + k)

并向其传递函数Vec Bool 2 -> Vec Bool 1,因此它通过简单统一知道n = 2m = 1。然后,因为add是通过左参数的递归来定义的,所以函数类型的其余部分从Vec A (2 + k) -> Vec A (1 + k)减少到Vec A (suc (suc k)) -> Vec A (suc k)。然后,Agda可以应用直接统一(扩展数字文字):

Vec A (suc (suc k)) -> Vec A (suc k)
Vec Bool (suc (suc (suc (suc zero)))) -> Vec Bool (suc (suc (suc zero)))

推断出k = 2

看着另一个:

postfixApp : {A : Set}{n m k : Nat} -> (Vec A n -> Vec A m) -> Vec A (k + n) -> Vec A (k + m)

唯一的区别是,xorV强制nm的已知数量为2和1,但这只会使您的函数类型的其余部分变为{{1 }}。此类型不会进一步减少,因为添加是通过第一个参数Vec A (k + 2) -> Vec A (k + 1)上的递归来定义的,此参数此时未知。然后,您尝试将kk + 2统一,4k + 1统一,Agda吐出黄色。 “但显然3,”你说!你知道,因为你知道数学,并且可以应用减法和其他简单的原理,但阿格达不知道这一点。 k = 2只是它的另一个功能,并且统一任意函数应用程序很难。如果我要求您将_+_(2 + x) * (2 + y)统一,该怎么办?是否应该预计类型检查器会对数字进行分解并抱怨没有唯一的因子分解?我猜因为乘法是可交换的,除非你限制边,否则通常不会有,但是Agda应该知道乘法是可交换的吗?

无论如何,因此Agda只知道如何进行统一,这基本上将“结构”数量相互匹配。数据构造函数对它们具有这种结构质量,类型构造函数也是如此,因此这些都可以明确地统一。当遇到比这更漂亮的东西时,你会遇到“高阶统一”问题,这个问题一般都无法解决。 Agda实现了一种名为米勒模式统一的奇特算法,它可以解决一些有限的各种情况,但有些事情是它无法做到的,而你的功能应用就是其中之一。

如果你查看标准库,你会发现大多数情况下类型涉及添加自然,其中一个加数(左边的)通常不会是隐式的,除非另一个参数完全指定它(就像697)中的情况一样。

至于如何应对,一般来说解决这个问题并不多。过了一段时间,你会对Agda可以推断出什么以及它不能推断出什么感觉产生一种感觉,然后停止隐藏无法解决的论点。您可以定义prefixApp的“对称”版本,但最终只是同样痛苦地使用它的两面,所以我也不建议这样做。

答案 1 :(得分:1)

实际上,可以使用几乎相同的类型定义此函数。

postfixApp : {A : Set}{n m k : ℕ} -> (Vec A n -> Vec A m) -> Vec A (n + k) -> Vec A (k + m)
postfixApp f xs with splitAt' (reverse xs)
... | ys , zs = reverse zs ++ f (reverse ys)

test-func : Vec Bool 3 -> Vec Bool 2
test-func (x1 ∷ x2 ∷ x3 ∷ []) = (x1 ∧ x2) ∷ (x2 ∨ x3) ∷ []

test : postfixApp test-func (false ∷ false ∷ true ∷ false ∷ true ∷ [])
                           ≡ false ∷ false ∷ false ∷ true ∷ []
test = refl

整个代码:http://lpaste.net/107176