为什么顺序在函数定义中很重要?

Why does order matter in function definition?

给定列表的这些定义:

data List (A : Set) : Set where
    []   : List A
    _::_ : (x : A)(xs : List A) → List A

length : {A : Set} → List A → ℕ
length []        = zero
length (_ :: xs) = suc (length xs)

lookup : {A : Set}(xs : List A)(n : ℕ) → (isTrue (n < length xs)) → A
lookup [] n ()
lookup (x :: xs) zero p = x
lookup (x :: xs) (suc n) p = lookup xs n p

和布尔值:

data Bool : Set where
    true  : Bool
    false : Bool

data False : Set where
record True : Set where

isTrue : Bool → Set
isTrue true     = True
isTrue false    = False

_<_ 定义为:

时编译没有错误
open import Data.Nat using (ℕ; suc; zero)

_<_ : ℕ → ℕ → Bool
_     < zero   = false
zero  < suc n  = true
suc m < suc n = m < n

但是如果我将前两个定义的顺序切换为:

_<_ : ℕ → ℕ → Bool
zero  < suc n  = true
_     < zero   = false
suc m < suc n = m < n

然后我得到错误:

isTrue (n < length []) should be empty, but that's not obvious to me
when checking that the clause lookup [] n () has type
{A : Set} (xs : List A) (n : ℕ) → isTrue (n < length xs) → A

这向我暗示 n < length []zero < suc n = true 匹配,这不应该,因为 length [] 等于 zerosuc m < suc n = m < n,这它不应该这样做,因为这是 _<_ 的最后一个定义(以及其他情况的相同推理)。

我是不是误解了 agda 与函数定义的匹配方式,或者这个荒谬的模式是如何工作的?

在内部,Agda 通过模式匹配将定义转换为案例树(请参阅 ReadTheDocs 处的文档)。为了构建这个案例树,Agda 总是首先查看第一个子句。因此,根据子句的顺序,生成的案例树可能会有所不同。在您的示例中, _<_ 的第一个定义的案例树是(伪语法):

m < n = case n of
          zero   -> false
          suc n' -> case m of
            zero   -> true
            suc m' -> m' < n'

_<_ 的第二个定义的案例树是

m < n = case m of
          zero   -> case n of
            zero   -> false
            suc n' -> true
          suc m' -> case n of
            zero   -> false
            suc m' -> m' < n'

实际上,在第二个定义中,Agda 将单个子句 _ < zero = false 拆分为两个子句 zero < zero = falsesuc m < zero = false,防止 n < zero 进一步减少。

要阻止 Agda 像这样拆分子句,您可以将 {-# OPTIONS --exact-split #-} 放在文件的顶部(或将 --exact-split 添加到 Agda 的命令行标志中)。

如果您想详细了解如何通过模式匹配将定义细化为案例树,您可以阅读 our upcoming ICFP paper 有关该主题的内容。

Jesper 的正确回复还指出了另一种使 lookup_<_ 的第二个定义一起工作的方法:只需自己匹配 n,这样 n 的哪个分支是明确的=12=] 取:

lookup : {A : Set}(xs : List A)(n : ℕ) → (isTrue (n < length xs)) → A
lookup [] zero ()
lookup [] (suc _) ()
lookup (x :: xs) zero p = x
lookup (x :: xs) (suc n) p = lookup xs n p