agda程序必须终止吗?

时间:2013-08-12 03:02:06

标签: emacs agda halting-problem

已经说过所有agda程序终止的几个地方。但是我可以构造一个这样的函数:

stall : ∀ n → ℕ
stall 0 = 0
stall x = stall x

语法高亮显示器似乎不喜欢它,但没有编译错误。

计算stall 0的正常形式会产生0。计算stall 1的结果会导致Emacs挂起看起来很像非终止循环。

这是一个错误吗?或者Agda有时会永远运行?或者是更微妙的事情?

2 个答案:

答案 0 :(得分:17)

实际上,存在编译错误。 agda可执行文件发现错误并将该信息传递给Emacs中的agda-mode,然后语法突出显示以告知您存在错误。我们可以看看如果直接使用agda会发生什么。这是我正在使用的文件:

module C1 where

open import Data.Nat

loop : ℕ → ℕ
loop 0 = 0
loop x = loop x

现在,我们调用agda -i../lib-0.7/src -i. C1.agda(不介意-i参数,他们只是让可执行文件知道在哪里查找标准库)我们得到错误:

Termination checking failed for the following functions:
  loop
Problematic calls:
  loop x
    (at D:\Agda\tc\C1.agda:7,10-14)

这确实是编译错误。这些错误阻止我们import将此模块与其他模块进行编译或编译。例如,如果我们将这些行添加到上面的文件中:

open import IO

main = run (putStrLn "")

使用C-c C-x C-c编译模块,agda-mode抱怨:

You can only compile modules without unsolved metavariables
or termination checking problems.

其他类型的编译错误包括类型检查问题:

module C2 where

open import Data.Bool
open import Data.Nat

type-error : ℕ → Bool
type-error n = n
__________________________

D:\Agda\tc\C2.agda:7,16-17
ℕ !=< Bool of type Set

when checking that the expression n has type Bool

积极性检查失败:

module C3 where

data Positivity : Set where
  bad : (Positivity → Positivity) → Positivity
__________________________

D:\Agda\tc\C3.agda:3,6-16
Positivity is not strictly positive, because it occurs to the left
of an arrow in the type of the constructor bad in the definition of
Positivity.

或未解决的元变量:

module C4 where

open import Data.Nat

meta : ∀ {a} → ℕ
meta = 0
__________________________

Unsolved metas at the following locations:
  D:\Agda\tc\C4.agda:5,11-12

现在,你正确地注意到一些错误是“死胡同”,而其他错误让你继续编写你的程序。那是因为有些错误比其他错误更糟糕。例如,如果你得到未解决的元变量,很可能你只能填写缺失的信息,一切都会好的。

至于挂起编译器:检查或编译模块不应导致agda循环。让我们尝试强制类型检查器循环。我们会在模块C1中添加更多内容:

data _≡_ {a} {A : Set a} (x : A) : A → Set a where
  refl : x ≡ x

test : loop 1 ≡ 1
test = refl

现在,要检查refl是否是该类型的正确表达式,agda必须评估loop 1。但是,由于终止检查失败,agda将不会展开loop(并以无限循环结束)。

然而,C-c C-n确实迫使agda尝试评估表达式(你基本上告诉它“我知道我在做什么”),所以你自然会陷入无限循环。


顺便提一下,如果禁用终止检查,则可以循环agda

{-# NO_TERMINATION_CHECK #-}
loop : ℕ → ℕ
loop 0 = 0
loop x = loop x

data _≡_ {a} {A : Set a} (x : A) : A → Set a where
  refl : x ≡ x

test : loop 1 ≡ 1
test = refl

最终:

stack overflow

根据经验:如果你可以通过检查(或编译)模块来进行agda循环而不使用任何编译器编译指示,那么这确实是一个错误,应该在bug tracker报告。话虽如此,如果您愿意使用编译器编译指示,很少有方法可以使非终止程序。我们已经看过{-# NO_TERMINATION_CHECK #-},以下是其他一些方法:

{-# OPTIONS --no-positivity-check #-}
module Boom where

data Bad (A : Set) : Set where
  bad : (Bad A → A) → Bad A

unBad : {A : Set} → Bad A → Bad A → A
unBad (bad f) = f

fix : {A : Set} → (A → A) → A
fix f = (λ x → f (unBad x x)) (bad λ x → f (unBad x x))

loop : {A : Set} → A
loop = fix λ x → x

这个依赖于非严格正面的数据类型。或者我们可以强制agda接受Set : Set(即Set的类型为Set本身)并重新构建Russell's paradox

{-# OPTIONS --type-in-type #-}
module Boom where

open import Data.Empty
open import Data.Product
open import Relation.Binary.PropositionalEquality

data M : Set where
  m : (I : Set) → (I → M) → M

_∈_ : M → M → Set
a ∈ m I f = Σ I λ i → a ≡ f i

_∉_ : M → M → Set
a ∉ b = (a ∈ b) → ⊥

-- Set of all sets that are not members of themselves.
R : M
R = m (Σ M λ a → a ∉ a) proj₁

-- If a set belongs to R, it does not contain itself.
lem₁ : ∀ {X} → X ∈ R → X ∉ X
lem₁ ((Y , Y∉Y) , refl) = Y∉Y

-- If a set does not contain itself, then it is in R.
lem₂ : ∀ {X} → X ∉ X → X ∈ R
lem₂ X∉X = (_ , X∉X) , refl

-- R does not contain itself.
lem₃ : R ∉ R
lem₃ R∈R = lem₁ R∈R R∈R

-- But R also contains itself - a paradox.
lem₄ : R ∈ R
lem₄ = lem₂ lem₃

loop : {A : Set} → A
loop = ⊥-elim (lem₃ lem₄)

source)。我们也可以写一个Girard悖论的变体,simplified by A.J.C. Hurkens

{-# OPTIONS --type-in-type #-}
module Boom where

⊥   = ∀ p → p
¬_  = λ A → A → ⊥
℘_  = λ A → A → Set
℘℘_ = λ A → ℘ ℘ A

U = (X : Set) → (℘℘ X → X) → ℘℘ X

τ : ℘℘ U → U
τ t = λ (X : Set) (f : ℘℘ X → X) (p : ℘ X) → t λ (x : U) → p (f (x X f))

σ : U → ℘℘ U
σ s = s U λ (t : ℘℘ U) → τ t

τσ : U → U
τσ x = τ (σ x)

Δ = λ (y : U) → ¬ (∀ (p : ℘ U) → σ y p → p (τσ y))
Ω = τ λ (p : ℘ U) → ∀ (x : U) → σ x p → p x

loop : (A : Set) → A
loop = (λ (₀ : ∀ (p : ℘ U) → (∀ (x : U) → σ x p → p x) → p Ω) →
  (₀ Δ λ (x : U) (₂ : σ x Δ) (₃ : ∀ (p : ℘ U) → σ x p → p (τσ x)) →
  (₃ Δ ₂ λ (p : ℘ U) → (₃ λ (y : U) → p (τσ y)))) λ (p : ℘ U) →
  ₀ λ (y : U) → p (τσ y)) λ (p : ℘ U) (₁ : ∀ (x : U) → σ x p → p x) →
  ₁ Ω λ (x : U) → ₁ (τσ x)
但是,这个真是一团糟。但它有一个很好的属性,它只使用依赖函数。奇怪的是,它甚至没有通过类型检查并导致agda循环。将整个loop术语拆分为两个帮助。

答案 1 :(得分:6)

您看到的语法突出显示是编译错误。终止检查器的作用是突出一种粉红橙色的非终止功能(&#34;鲑鱼&#34;)。您可能会注意到无法从其他模块导入包含此类错误的模块。它也不能编译成Haskell。

所以,是的,Agda程序总是终止,这不是一个bug。