有没有办法方便地使用EqReasoning
的多个实例化,其中基础Setoid
不一定是语义相等(即≡-Reasoning
不能使用)? ≡-Reasoning
方便的原因是type参数是隐式的,它通过自动选择语义相等来唯一地确定正在使用的Setoid
。使用任意Setoid
时,没有这样的唯一选择。然而,许多结构提供了一种提升Setoid
:
Data.Maybe
和Data.Covec
有setoid
成员。Data.Vec.Equality.Equality
提供了足够的定义来为Setoid
编写规范Vec
解除。有趣的是,在Relation.Binary.Vec.Pointwise
可用的等式略有不同,但它并没有提供直接提升,尽管实现了所有必要的位。Data.List
在Setoid
中发送了Relation.Binary.List.Pointwise
。Data.Container
也知道如何举起Setoid
。通过使用这些结构中的任何一个,即使一个人开始使用,也会自动使用多个Setoid
。当证明使用这些结构(在单个证明中使用多个结构)时,很难记下证明,因为EqReasoning
必须为所有这些结构实例化,即使每个特定的Setoid
都很明显。这可以通过重命名begin_
,_≈⟨_⟩_
和_∎
来完成,但我不认为这种重命名很方便。
例如,考虑Setoid
Maybe
中Data.Maybe.Eq.just
的证据,其中一系列参数需要包含在cong just
中(想想Setoid
)或一个证明临时需要在just
构造函数中包含事物的{{1}}任意{{1}}。
答案 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提供的实例参数。然而,这项技术使它更加方便。