方便地使用多个EqReasoning实例化

时间:2014-01-17 09:57:02

标签: agda reasoning

有没有办法方便地使用EqReasoning的多个实例化,其中基础Setoid不一定是语义相等(即≡-Reasoning不能使用)? ≡-Reasoning方便的原因是type参数是隐式的,它通过自动选择语义相等来唯一地确定正在使用的Setoid。使用任意Setoid时,没有这样的唯一选择。然而,许多结构提供了一种提升Setoid

的规范方法
  • Data.MaybeData.Covecsetoid成员。
  • Data.Vec.Equality.Equality提供了足够的定义来为Setoid编写规范Vec解除。有趣的是,在Relation.Binary.Vec.Pointwise可用的等式略有不同,但它并没有提供直接提升,尽管实现了所有必要的位。
  • Data.ListSetoid中发送了Relation.Binary.List.Pointwise
  • Data.Container也知道如何举起Setoid

通过使用这些结构中的任何一个,即使一个人开始使用,也会自动使用多个Setoid。当证明使用这些结构(在单个证明中使用多个结构)时,很难记下证明,因为EqReasoning必须为所有这些结构实例化,即使每个特定的Setoid都很明显。这可以通过重命名begin__≈⟨_⟩__∎来完成,但我不认为这种重命名很方便。

例如,考虑Setoid MaybeData.Maybe.Eq.just的证据,其中一系列参数需要包含在cong just中(想想Setoid)或一个证明临时需要在just构造函数中包含事物的{{1}}任意{{1}}。

2 个答案:

答案 0 :(得分:3)

通常情况下,Agda可以为您选择的唯一方法是由上下文唯一确定的方式。在EqReasoning的情况下,通常没有足够的信息来确定Setoid,实际上更糟糕的是:您可以在同一Setoid上有两个不同的Carrier_≈_(例如,考虑trans字段中isEquivalence itivity的两个定义不等的证明。


但是,Agda确实允许使用特殊形式的隐式参数,只要只有一个所需类型的值,就可以填充它们。这些被称为实例参数(认为实例与Haskell类型类实例一样)。

大致展示其工作原理:

postulate
  A : Set
  a : A

现在,实例参数包含在双花括号{{}}中:

elem : {{x : A}} → A
elem {{x}} = x

如果我们决定稍后在某个地方使用elem,Agda会检查范围内A类型的任何值,如果只有其中一个,则会将{{1}填入}}。如果我们添加:

{{x : A}}

Agda现在会抱怨:

postulate
  b : A

因为Agda已经允许你在类型级别上执行计算,所以实例参数在他们能做的事情上是有意限制的,即Agda不会执行递归搜索来填充它们。例如考虑:

Resolve instance argument _x_7 : A. Candidates: [a : A, b : A]

您的问题中提及eq : ... → IsEquivalence _≈_ → IsEquivalence (Eq _≈_) Eq。然后,当您要求Agda填写Data.Maybe.Eq类型的实例参数时,它不会尝试查找IsEquivalence (Eq _≈_)类型的内容并将IsEquivalence _≈_应用于它。


有了这个,让我们看一下可行的方法。但是,请记住,所有这些都是统一的,因此你可能需要在这里和那里将它推向正确的方向(如果你正在处理的类型变得复杂,统一可能需要你给它这么多指示,它最终是不值得的。)

就个人而言,我发现实例参数有点脆弱,我通常会避开它们(并且通过快速检查,标准库似乎也是如此),但您的体验可能会有所不同。

无论如何,我们走了。我构建了一个(完全荒谬的)示例来演示如何做到这一点。首先是一些样板:

eq

为了使这个例子自成一体,我写了一些以自然数作为载体的open import Data.Maybe open import Data.Nat open import Relation.Binary import Relation.Binary.EqReasoning as EqR

Setoid

现在,data _≡ℕ_ : ℕ → ℕ → Set where z≡z : 0 ≡ℕ 0 s≡s : ∀ {m n} → m ≡ℕ n → suc m ≡ℕ suc n ℕ-setoid : Setoid _ _ ℕ-setoid = record { _≈_ = _≡ℕ_ ; isEquivalence = record { refl = refl ; sym = sym ; trans = trans } } where refl : Reflexive _≡ℕ_ refl {zero} = z≡z refl {suc _} = s≡s refl sym : Symmetric _≡ℕ_ sym z≡z = z≡z sym (s≡s p) = s≡s (sym p) trans : Transitive _≡ℕ_ trans z≡z q = q trans (s≡s p) (s≡s q) = s≡s (trans p q) 模块在​​EqReasoning上进行参数化,所以通常你会这样做:

Setoid

但是,我们希望open EqR ℕ-setoid 参数是隐式的(实例)而不是显式的,所以我们定义并打开一个虚拟模块:

Setoid

我们可以写出这个简单的证据:

open module Dummy {c ℓ} {{s : Setoid c ℓ}} = EqR s

请注意,我们从来没有必须指定idʳ : ∀ n → n ≡ℕ (n + 0) idʳ 0 = z≡z idʳ (suc n) = begin suc n ≈⟨ s≡s (idʳ n) ⟩ suc (n + 0) ∎ ,实例参数将其选中,因为它是唯一的类型正确值。

现在,让我们稍微调整一下。我们会在混合中添加ℕ-setoid。同样,因为实例参数执行递归搜索,我们必须自己定义setoid:

Data.Maybe.setoid

我将假设几个愚蠢的事情只是为了证明Agda确实选择了正确的setoids:

Maybeℕ-setoid = setoid ℕ-setoid
_≡M_ = Setoid._≈_ Maybeℕ-setoid

答案 1 :(得分:1)

我想到了使用实例参数替代建议的解决方案,这些参数稍微弯曲了需求,但符合我的目的。问题的主要负担是必须多次明确地打开EqReasoning,特别是必须为包含的符号创建新名称。稍微改进的是每个关系证明传递正确的Setoid一次。换句话说,以某种方式将其传递给begin__∎。然后我们可以为所有其他函数隐含Setoid

import Relation.Binary.EqReasoning as EqR
import Relation.Binary using (Setoid)
module ExplicitEqR where
 infix 1 begin⟨_⟩_
 infixr 2 _≈⟨_⟩_ _≡⟨_⟩_
 infix 2 _∎
 begin⟨_⟩_ : ∀ {c l} (X : Setoid c l) → {x y : Setoid.Carrier X} → EqR._IsRelatedTo_ X x y → Setoid._≈_ X x y
 begin⟨_⟩_ X p = EqR.begin_ X p
 _∎ : ∀ {c l} {X : Setoid c l} → (x : Setoid.Carrier X) → EqR._IsRelatedTo_ X x x
 _∎ {X = X} = EqR._∎ X
 _≈⟨_⟩_ : ∀ {c l} {X : Setoid c l} → (x : Setoid.Carrier X) → {y z : Setoid.Carrier X} → Setoid._≈_ X x y → EqR._IsRelatedTo_ X y z → EqR._IsRelatedTo_ X x z
 _≈⟨_⟩_ {X = X} = EqR._≈⟨_⟩_ X
 _≡⟨_⟩_ : ∀ {c l} {X : Setoid c l} → (x : Setoid.Carrier X) → {y z : Setoid.Carrier X} → x ≡ y → EqR._IsRelatedTo_ X y z → EqR._IsRelatedTo_ X x z
 _≡⟨_⟩_ {X = X} = EqR._≡⟨_⟩_ X

重用Vitus回答中的好例子,我们可以写出来:

lem : ∀ n → just (n + 0) ≡M nothing
lem n = begin⟨ Data.Maybe.setoid ℕ-setoid ⟩
  just (n + 0)  ≈⟨ just
    (begin⟨ ℕ-setoid ⟩
      n + 0  ≈⟨ comm n 0 ⟩
      n      ≈⟨ eq0 n ⟩
      0      ∎
    )⟩
  just 0        ≈⟨ eq∅ ⟩
  nothing       ∎
  where open ExplicitEqR

仍然需要提及正在使用的Setoid,以避免使用Vitus提供的实例参数。然而,这项技术使它更加方便。

相关问题