合并具有大小类型的排序列表

时间:2014-02-04 22:29:39

标签: merge sortedlist termination agda

假设我们有一个排序列表的数据类型,与证明无关的排序证人。我们将使用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)

这个功能看起来很无害,除了说服阿格达这个功能总是很棘手。实际上,如果没有任何明确的大小索引,该函数将无法终止检查。一个选项是split the function into two mutually recursive definitions。这可行,但为定义和相关证明添加了一定量的冗余。

但同样地,我不确定是否有可能明确地给出大小索引,以便merge具有Agda将接受的签名。 These slides明确地讨论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的第一个版本没有终止检查,因为the following does

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

1 个答案:

答案 0 :(得分:2)

有趣的是,你的第一个版本实际上是正确的。我提到Agda在考虑Size时只提供了一些额外的规则,其中一个是↑ ∞ ≡ ∞。顺便说一下,您可以通过以下方式确认:

↑inf : ↑ ∞ ≡ ∞
↑inf = refl

嗯,这促使我调查其他规则是什么。我在Andreas Abel的大小类型的幻灯片中找到了其余的(可以找到here):

  • ↑ ∞ ≡ ∞
  • i ≤ ↑ i ≤ ∞
  • T {i} <: T {↑ i} <: T {∞}

<:关系是子类型关系,您可以从面向对象的语言中了解它。还有一个与此关系相关的规则,包含规则:

Γ ⊢ x : A   Γ ⊢ A <: B
────────────────────── (sub)
      Γ ⊢ x : B

因此,如果您的x类型为A且您知道AB的子类型,则可以将x视为属于B类型。这看起来很奇怪,因为遵循大小类型的子类型规则,您应该能够将SortedList l u {ι}类型的值视为SortedList l u

所以我做了一点挖掘并找到了这个bug report。事实上,问题只是Agda没有正确识别尺寸而且规则不会触发。我需要做的就是将SortedList的定义重写为:

data SortedList (l u : A) : {ι : Size} → Set ( ⊔ ℓ) where
  -- ...

就是这样!


作为附录,这是我用于测试的代码:

data ℕ : {ι : _} → Set where         -- does not work
-- data ℕ : {ι : Size} → Set where   -- works
  zero : ∀ {ι} →         ℕ {↑ ι}
  suc  : ∀ {ι} → ℕ {ι} → ℕ {↑ ι}

test : ∀ {ι} → ℕ {ι} → ℕ
test n = n