我想对两种样式进行对比,以便为Agda中的代数结构声明新的记录类型。
遵循标准Agda包Algebra
中使用的样式,可以按如下方式定义BoundedJoinSemilattice
:
record IsBoundedJoinSemilattice {a ℓ} {A : Set a}
(_≈_ : Rel A ℓ) (_∨_ : Op₂ A) (⊥ : A) : Set (a Level.⊔ ℓ) where
field
isCommutativeMonoid : IsCommutativeMonoid _≈_ _∨_ ⊥
idem : Idempotent _≈_ _∨_
open IsCommutativeMonoid isCommutativeMonoid public
record BoundedJoinSemilattice c ℓ : Set (suc (c ⊔ ℓ)) where
infixr 6 _∨_
infix 4 _≈_
field
Carrier : Set c
_≈_ : Rel Carrier ℓ
_∨_ : Op₂ Carrier
⊥ : Carrier
isBoundedJoinSemilattice : IsBoundedJoinSemilattice _≈_ _∨_ ⊥
open IsBoundedJoinSemilattice isBoundedJoinSemilattice public
commutativeMonoid : CommutativeMonoid _ _
commutativeMonoid = record {
Carrier = Carrier;
_≈_ = _≈_;
_∙_ = _∨_;
ε = ⊥;
isCommutativeMonoid = isCommutativeMonoid
}
使用此方法,重叠(最多重命名)BoundedJoinSemiLattice
的任何字段与其他更抽象结构(如Setoid
,Semigroup
的字段重叠, Monoid
中的CommutativeMonoid
和BoundedJoinSemiLattice
重复。为了将BoundedJoinSemiLattice
视为其“超类型”之一,必须调用一个投影函数来负责将其字段映射到超类型的字段,例如上面的commutativeMonoid
函数。
然而,这个字段重复可能会导致代码中的重要样板,从而根据不太具体的代数结构构建更具体的代数结构。更自然的定义可能是这样的(将CommutativeMonoid
重命名为CM
):
record IsBoundedJoinSemilattice {c ℓ} (cm : CM c ℓ)
(⊥ : CM.Carrier cm) : Set (c Level.⊔ ℓ) where
field
isCommutativeMonoid : IsCM (CM._≈_ cm) (CM._∙_ cm) ⊥
idem : Idempotent (CM._≈_ cm) (CM._∙_ cm)
open IsCommutativeMonoid isCommutativeMonoid public
record BoundedJoinSemilattice c ℓ : Set (suc (c ⊔ ℓ)) where
field
commutativeMonoid : CM c ℓ
isBoundedJoinSemilattice : IsBoundedJoinSemilattice commutativeMonoid (CM.ε commutativeMonoid)
open CommutativeMonoid commutativeMonoid public using (Carrier; _≈_) renaming (
_∙_ to _∨_;
ε to ⊥
)
open IsBoundedJoinSemilattice isBoundedJoinSemilattice public
此处,我们的想法是不将CommutativeMonoid
的字段复制到BoundedJoinSemilattice
中,而是将后者声明为CommutativeMonoid
类型的单个字段。然后,我们使用open...public
将其子字段“继承”到包含记录中。实际上,这正是Algebra.Structures
中标准库中其他地方使用的惯用语,除了在这里我们还重命名了继承的字段,以便在继承的上下文中对它们进行适当的命名。
不仅第二种方法不那么冗余,而且现在想要从BoundedJoinSemilattice
构造CommutativeMonoid
的客户端代码可以简单地将其作为参数传递给正在构造的记录。另一方面,想要从头构建BoundedJoinSemilattice
的客户端代码现在必须构造一个中间CommutativeMonoid
。
是否有Algebra
模块不使用此继承样式的原因,但Algebra.Structures
会这样做?也许第二种方法存在问题,我没有发现,或者它没有太大区别:例如,使用第一种方法,也许可以简单地定义一个“构造函数”函数来处理{{{来自BoundedJoinSemiLattice
的1}},以恢复第二种方法的大部分便利。
答案 0 :(得分:3)
我在第二种方法中看到的主要问题是你不能编写(“继承”)多个结构。让我用CommutativeSemiring
说明一点,Algebra.Structures
的定义需要两个IsCommutativeMonoid
s:
record IsCommutativeSemiring
{a ℓ} {A : Set a} (≈ : Rel A ℓ)
(_+_ _*_ : Op₂ A) (0# 1# : A) : Set (a ⊔ ℓ) where
open FunctionProperties ≈
field
+-isCommutativeMonoid : IsCommutativeMonoid ≈ _+_ 0#
*-isCommutativeMonoid : IsCommutativeMonoid ≈ _*_ 1#
distribʳ : _*_ DistributesOverʳ _+_
zeroˡ : LeftZero 0# _*_
-- ...
现在假设我们使用了您提出的解决方案。以下是IsCommutativeSemiring
的样子:
record IsCommSemiring {c ℓ}
(+-cm : CommutativeMonoid c ℓ)
(*-cm : CommutativeMonoid c ℓ) : Set (c ⊔ ℓ) where
open CommutativeMonoid +-cm
using (_≈_)
renaming (_∙_ to _+_; ε to 0#)
open CommutativeMonoid *-cm
using ()
renaming (_∙_ to _*_; ε to 1#)
open FunProps _≈_
-- more stuff goes here
现在你遇到了严重问题:你不知道相应Carrier
的{{1}}是什么,但它们最好是同一类型。所以你必须做出这个丑陋的步骤:
CommutativeMonoid
然后,在record IsCommSemiring {c ℓ}
(+-cm : CommutativeMonoid c ℓ)
(*-cm : CommutativeMonoid c ℓ) : Set (suc (c ⊔ ℓ)) where
open CommutativeMonoid +-cm
using (_≈_)
renaming (Carrier to +-Carrier; _∙_ to _+_; ε to 0#)
open CommutativeMonoid *-cm
using ()
renaming (Carrier to *-Carrier; _∙_ to _*′_; ε to 1#′; _≈_ to _≈′_)
open FunProps _≈_
field
carriers : *-Carrier ≡ +-Carrier
的帮助下,您必须定义适用于subst
的{{1}}:
_*_
最后,你可以写出分配领域:
+-Carrier
这看起来很尴尬,但情况变得更糟:基本的平等也应该是一样的!这一开始似乎不是一个大问题,你可以只需要 _*_ : (x y : +-Carrier) → +-Carrier
_*_ = subst (λ A → A → A → A) carriers _*′_
(实际上, field
distribʳ : _*_ DistributesOverʳ _+_
),但是当有人试图使用这些证据时,他们会感到惊讶。事实上,您可能会成为第一个使用这些证明的人。查看_≈_ ≡ _≈′_
中的_≈_ ≡ subst (λ A → A → A → Set ℓ) carriers _≈′_
,我们会找到以下代码:
IsCommutativeSemiring
如果您尝试使用您的版本编写,那么您将拥有Algebra.Structures
。在这一点上你唯一能做的就是将所有使用 distrib : _*_ DistributesOver _+_
distrib = (distribˡ , distribʳ)
where
distribˡ : _*_ DistributesOverˡ _+_
distribˡ x y z = begin
(x * (y + z)) ≈⟨ *-comm x (y + z) ⟩
((y + z) * x) ≈⟨ distribʳ x y z ⟩
((y * x) + (z * x)) ≈⟨ *-comm y x ⟨ +-CM.∙-cong ⟩ *-comm z x ⟩
((x * y) + (x * z)) ∎
的证据重写为subst
形式(再次,大量的_≈′_
) - 这就提出了一个问题:是吗?还值得吗?
用“构造函数”功能考虑你的想法:这当然是可行的。但是,当你想要组成多个结构时,你会遇到问题。
以下是如何从_≈_
subst
Monoid
实际上,Semigroup
唯一地确定了大部分记录(semigroup→monoid : ∀ {c ℓ} (s : Semigroup c ℓ) →
let open Semigroup s
open FunProps _≈_
in (ε : Carrier) (identity : Identity ε _∙_) → Monoid c ℓ
semigroup→monoid s ε id = record
{ Carrier = Carrier
; _≈_ = _≈_
; _∙_ = _∙_
; ε = ε
; isMonoid = record
{ isSemigroup = isSemigroup
; identity = id
}
}
where
open Semigroup s
,isSemigroup
和Carrier
),_≈_
也确定了_∙_
,所以我们甚至可以写:
id
实际上非常简洁。