假设我们有一个排序列表的数据类型,具有与证明无关的排序见证。我们将使用 Agda 的实验性大小类型功能,这样我们就有希望在数据类型上获得一些递归函数来通过 Agda 的终止检查器。
{-# OPTIONS --sized-types #-}
open import Relation.Binary
open import Relation.Binary.PropositionalEquality as P
module ListMerge
{???? ℓ}
(A : Set ????)
{_<_ : Rel A ℓ}
(isStrictTotalOrder : IsStrictTotalOrder _≡_ _<_) where
open import Level
open import Size
data SortedList (l u : A) : {ι : _} → Set (???? ⊔ ℓ) where
[] : {ι : _} → .(l < u) → SortedList l u {↑ ι}
_∷[_]_ : {ι : _} (x : A) → .(l < x) → (xs : SortedList x u {ι}) →
SortedList l u {↑ ι}
现在,人们想要在这样的数据结构上定义的一个经典函数是merge
,它接受两个排序列表,并输出一个完全包含输入列表元素的排序列表。
open IsStrictTotalOrder isStrictTotalOrder
merge : ∀ {l u} → SortedList l u → SortedList l u → SortedList l u
merge xs ([] _) = xs
merge ([] _) ys = ys
merge (x ∷[ l<x ] xs) (y ∷[ l<y ] ys) with compare x y
... | tri< _ _ _ = x ∷[ l<x ] (merge xs (y ∷[ _ ] ys))
merge (x ∷[ l<x ] xs) (.x ∷[ _ ] ys) | tri≈ _ P.refl _ =
x ∷[ l<x ] (merge xs ys)
... | tri> _ _ _ = y ∷[ l<y ] (merge (x ∷[ _ ] xs) ys)
这个函数看起来没什么害处,只是要让 Agda 相信它是完整的可能很棘手。事实上,如果没有任何显式的大小索引,该函数将无法进行终止检查。一种选择是将函数拆分为两个相互递归的定义 https://stackoverflow.com/questions/17910737/termination-check-on-list-merge。这是可行的,但给定义和相关证明增加了一定量的冗余。
但同样,我不确定是否可以明确给出大小索引,以便merge
有 Agda 会接受的签名。这些幻灯片 http://www2.tcs.ifi.lmu.de/~abel/appsemTalk.ps.gz%E2%80%8E讨论mergesort
明确地;那里给出的签名表明以下内容应该有效:
merge′ : ∀ {l u} → {ι : _} → SortedList l u {ι} →
{ι′ : _} → SortedList l u {ι′} → SortedList l u
merge′ xs ([] _) = xs
merge′ ([] _) ys = ys
merge′ (x ∷[ l<x ] xs) (y ∷[ l<y ] ys) with compare x y
... | tri< _ _ _ = x ∷[ l<x ] (merge′ xs (y ∷[ _ ] ys))
merge′ (x ∷[ l<x ] xs) (.x ∷[ _ ] ys) | tri≈ _ P.refl _ =
x ∷[ l<x ] (merge′ xs ys)
... | tri> _ _ _ = y ∷[ l<y ] (merge' (x ∷[ _ ] xs) ys)
这里我们所做的是允许输入具有任意(和不同)的大小,并指定输出的大小为 ∞。
不幸的是,有了这个签名,Agda 抱怨说.ι != ∞ of type Size
、检查身体时xs
定义的第一个子句。我并不声称非常了解大小类型,但我的印象是任何大小 ι 都会与 ∞ 统一。也许自这些幻灯片编写以来,大小类型的语义已经发生了变化。
那么,我的场景是针对哪种大小的类型的用例吗?如果是这样,我应该如何使用它们?如果尺寸类型在这里不合适,为什么第一个版本merge
上面不是终止检查,因为以下是 https://stackoverflow.com/questions/17910737/termination-check-on-list-merge:
open import Data.Nat
open import Data.List
open import Relation.Nullary
merge : List ℕ → List ℕ → List ℕ
merge (x ∷ xs) (y ∷ ys) with x ≤? y
... | yes p = x ∷ merge xs (y ∷ ys)
... | _ = y ∷ merge (x ∷ xs) ys
merge xs ys = xs ++ ys