如何在前提条件下发挥Adga功能

时间:2018-12-05 17:37:23

标签: agda theorem-proving

我想对自然数进行减法运算。但是,该函数的参数有一个前提,即N中的a,b全部; a> = b。所以我做了一些相关的功能:

data ℕ : Set where
    zero : ℕ
    suc : ℕ → ℕ

data _NotLessThan_ : (n m : ℕ) → Set where
    ZERO : zero NotLessThan zero
    SUC_ZERO : ∀ n → (suc n) NotLessThan zero
    STEP : ∀ m n → m NotLessThan n → (suc m) NotLessThan (suc n)

sub : (m n : ℕ) →  (a : m NotLessThan n) → ℕ
    sub zero (suc n) () -- zero - n can't return
    sub zero zero _ = zero
    sub (suc m) zero _ = (suc m)
    sub (suc x) (suc y) a = sub x y (x NotLessThan y)

但是,我得到了错误:

 Set !=< x NotLessThan y of type Set₁
 when checking that the expression x NotLessThan y has type
 x NotLessThan y

我发现我的类型是x NotLessThan y。是否有任何类型错误?如何调试这种类型错误或如何声明一个函数以跳过类型检测错误?

2 个答案:

答案 0 :(得分:3)

表达式(x NotLessThan y)的类型不是(x NotLessThan y)NotLessThan是类型集(索引索引)的数据定义。您使用定义的三个构造函数构造NotLessThan的元素。在这种情况下,您必须在a上进行模式补丁,以获取适当的构造函数和所需类型的元素。所以最后一种情况是

sub (suc x) (suc y) (STEP _ _ a) = sub x y a

答案 1 :(得分:1)

简而言之

您的代码段中存在类型不兼容的问题,即(x NotLessThan y)是类型,而不是类型(x NotLessThan y)的值,必须提供这些值才能使函数正确输入类型。以下代码片段可解决此问题:

sub : (m n : ℕ) →  (a : m NotLessThan n) → ℕ
sub .zero .zero ZERO = zero
sub .(suc n) .zero SUC n ZERO = suc n
sub .(suc m) .(suc n) (STEP m n a) = sub m n a

很久

这是代码中的主要问题,但也有一些不正确之处使您无法编写正确的代码。这些如下:

  • 可以对您的数据类型名称_NotLessThan_进行一些改进,以更好地匹配数学概念(并匹配标准库中的内容)。

  • 构造函数通常不使用大写字母。

  • 您提供了两个具有相同目的的构造函数ZEROSUC_ZERO,也就是说,任何自然数都大于或等于零。这些可以分解。

  • 您的第二个构造函数SUC_ZERO包含一个下划线,Agda会将其解释为放置该构造函数的参数的位置。我觉得这不是故意的,在这种情况下,您绝对应该避免在构造函数名称中使用下划线。

  • 在函数sub中,尽管第三个参数(a大于b的证明)已经包含进行计算所需的信息,但您还是将两个自然变量进行了案例分割。相反,对此参数进行模式匹配也将避免任何空的情况。

考虑到这些注释,将为您的数据类型提供以下定义:

data _≤_ : ℕ → ℕ → Set where
  z≤s : ∀ {a} → zero ≤ a
  s≤s : ∀ {a b} → a ≤ b → suc a ≤ suc b

然后使用这种数据类型非常自然地编写子函数:

sub' : ∀ a b → b ≤ a → ℕ
sub' a .zero z≤s = a
sub' ._ ._ (s≤s p) = sub' _ _ p

请注意,在此代码段中,下划线(和定义左侧的点)用于标记参数,这些参数的值可以由Agda推断,这使事实证明证明足以计算减法。

走得更远

让Agda单独计算证明而不必在要进行减法时必须提供证明,这可能很有趣。为此,必须证明您的数据类型是可判定的,这意味着,给定两个自然数ab,必须编写一个函数来计算{{1 }或否定它。这需要几个步骤,第一步是定义逻辑假(空类型):

a ≤ b

根据这种数据类型,可以定义逻辑取反:

data ⊥ : Set where

我们现在可以定义可判定类型的类型(大致可以证明是对还是错):

¬ : Set → Set
¬ A = A → ⊥

然后,我们可以证明对于任何输入,根据此定义,可以决定是较小还是相等的数据类型:

data Dec (A : Set) : Set where
  yes : A → Dec A
  no : ¬ A → Dec A

为了封装错误情况以进行减法运算,我们需要mayad monad,它可以是错误情况,也可以是值:

_≤?_ : ∀ a b → Dec (a ≤ b)
zero ≤? b = yes z≤s
suc a ≤? zero = no (λ ())
suc a ≤? suc b with a ≤? b
suc a ≤? suc b | yes p = yes (s≤s p)
suc a ≤? suc b | no ¬p = no λ {(s≤s q) → ¬p q}

最后,我们可以编写一个减去两个自然数的减法,检查是否满足减法要求,如果不满足则返回错误,并在可能的情况下进行计算:

data Maybe (A : Set) : Set where
  nothing : Maybe A
  just : A → Maybe A

作为失败的一个例子,这是一个不合理的减法:

_-M_ : (a b : ℕ) → Maybe ℕ
a -M b with b ≤? a
a -M b | yes p = just (sub' a b p)
a -M b | no _ = nothing

Agda在评估error : Maybe ℕ error = suc (suc (zero)) -M (suc (suc (suc (suc (suc (suc zero)))))) 时会给出以下内容:

error

作为成功的例子,这里有一个声音减法:

nothing

Agda在评估success : Maybe ℕ success = (suc (suc (suc (suc (suc (suc zero)))))) -M suc (suc (zero)) 时会给出以下内容:

success

请注意,所有这些数量(直到可能定义数据类型之前)都可以在标准库中找到,并且在介绍它们时,我故意省略了Universe级别。