终止检查agda中的递归函数调用

时间:2015-06-06 23:25:08

标签: recursion agda termination coinduction

以下代码在Haskell中完全没问题:

dh :: Int -> Int -> (Int, Int)
dh d q = (2^d, q^d)
a = dh 2 (fst b)
b = dh 3 (fst a)

Agda中的类似代码无法编译(终止检查失败):

infixl 9 _^_
_^_ : ℕ → ℕ → ℕ
x ^ zero = 1
x ^ suc n = x * (x ^ n)

dh : ℕ -> ℕ -> ℕ × ℕ
dh d q = 2 ^ d , q ^ d

mutual
  a = dh 2 (proj₁ b)
  b = dh 3 (proj₁ a)

a的定义使用a,它在结构上不小,因此是循环。似乎终止检查器不会查看dh的定义。

我尝试过使用coinduction,设置选项--termination-depth=4 - 没有帮助。 在{-# NO_TERMINATION_CHECK #-}块中插入mutual会有所帮助,但它看起来像是作弊。

有没有一种干净的方法让Agda编译代码? Agda的终止检查器是否有一些基本限制?

1 个答案:

答案 0 :(得分:0)

Agda并不像Haskell那样假设懒惰的评价。相反,Agda要求所有表达式强正常化。这意味着无论您评估子表达式的顺序如何,都应该得到相同的答案。您提供的定义并非强烈规范化,因为评估订单不会终止:

a
-->
dh 2 (proj₁ b)
-->
dh 2 (proj₁ (dh 3 (proj₁ a))
-->
dh 2 (proj₁ (dh 3 (proj₁ (dh 2 (proj₁ b)))))

特别是,Agda的JavaScript后端会为ab生成不会终止的代码,因为JavaScript是经过严格评估的。

Agda's termination checker检查强规范化程序的子集:那些只有结构递归的程序。它查看函数定义中模式匹配的构造函数的数量和顺序,以确定递归调用是否使用"较小的"参数。 ab没有任何参数,因此终止检查器会看到从aa(通过b)的嵌套调用相同的"尺寸"作为a本身。