我在终止检查时遇到问题,与此问题中描述的问题以及此Agda bug report/feature request中描述的问题非常相似。
问题是说服编译器以下unionWith
终止。使用重复键的组合函数,unionWith
合并表示为按键排序的(键,值)对列表的两个映射。有限映射的 Key 参数是映射中包含的键的(非紧)下限。(定义这种数据类型的一个原因是提供一个语义域,我可以在其中解释 AVL 树,以证明它们的各种属性。)
open import Function
open import Relation.Binary renaming (IsEquivalence to IsEq)
open import Relation.Binary.PropositionalEquality as P using (_≡_)
module FiniteMap
{k v ℓ ℓ′}
{Key : Set k}
(Value : Set v)
{_<_ : Rel Key ℓ}
(isStrictTotalOrder : IsStrictTotalOrder _≡_ _<_)
{_≈_ : Rel Value ℓ′}
(isEquivalence : IsEq _≈_)
where
open import Algebra.FunctionProperties
open import Data.Product
open IsStrictTotalOrder isStrictTotalOrder
open import Level
KV : Set (k ⊔ v)
KV = Key × Value
data FiniteMap (l : Key) : Set (k ⊔ v ⊔ ℓ) where
[] : FiniteMap l
_∷_ : (kv : KV) → let k = proj₁ kv in l < k → (m : FiniteMap k) → FiniteMap l
unionWith : ∀ {l} → Op₂ Value → Op₂ (FiniteMap l)
unionWith _ [] [] = []
unionWith _ [] m = m
unionWith _ m [] = m
unionWith _⊕_ (_∷_ (k , v) k<l m) (_∷_ (k′ , v′) k′<l m′) with compare k k′
... | tri< k<k′ _ _ = _∷_ (k , v) k<l (unionWith _⊕_ m (_∷_ (k′ , v′) k<k′ m′))
... | tri≈ _ k≡k′ _ rewrite P.sym k≡k′ = {!!} --_∷_ (k , v ⊕ v′) k<l (unionWith _⊕_ m m′)
... | tri> _ _ k′<k = _∷_ (k′ , v′) k′<l (unionWith _⊕_ (_∷_ (k , v) k′<k m) m′)
我无法将引用问题中讨论的解决方案推广到我的设置。例如,如果我引入一个辅助函数unionWith'
,它与 相互递归定义unionWith
,在这种情况下从后者调用k' < k
:
unionWith : ∀ {l} → Op₂ Value → Op₂ (FiniteMap l)
unionWith′ : ∀ {l} → Op₂ Value → (kv : KV) → let k = proj₁ kv in l < k → FiniteMap k → Op₁ (FiniteMap l)
unionWith _ [] [] = []
unionWith _ [] m = m
unionWith _ m [] = m
unionWith _⊕_ (_∷_ (k , v) k<l m) (_∷_ (k′ , v′) k′<l m′) with compare k k′
... | tri< k<k′ _ _ = _∷_ (k , v) k<l (unionWith _⊕_ m (_∷_ (k′ , v′) k<k′ m′))
... | tri≈ _ k≡k′ _ rewrite P.sym k≡k′ = {!!} --_∷_ (k , v ⊕ v′) k<l (unionWith _⊕_ m m′)
... | tri> _ _ k′<k = _∷_ (k′ , v′) k′<l (unionWith′ _⊕_ (k , v) k′<k m m′)
unionWith′ _ (k , v) l<k m [] = _∷_ (k , v) l<k m
unionWith′ _⊕_ (k , v) l<k m (_∷_ (k′ , v′) k′<l m′) with compare k k′
... | tri< k<k′ _ _ = {!!}
... | tri≈ _ k≡k′ _ = {!!}
... | tri> _ _ k′<k = _∷_ (k′ , v′) k′<l (unionWith′ _⊕_ (k , v) k′<k m m′)
然后,一旦我通过将第一个丢失的案例替换unionWith'
为所需的调用来打结递归结unionWith
,它就无法终止检查。
我也尝试引入额外with
的模式,但这很复杂,因为需要compare
在递归调用中使用结果。with
(如果我使用似乎对终止检查器没有帮助的嵌套子句。)
有没有办法使用with
模式或辅助功能来进行编译?这似乎是一个足够简单的情况,所以我希望这只是一个知道正确技巧的例子。
(也许 Agda 开发分支中的新终止检查器可以处理这个问题,但我想避免安装开发版本,除非我必须这样做。)