声明代数结构的记录类型的推荐惯例

时间:2014-01-09 09:36:27

标签: inheritance record agda

我想对两种样式进行对比,以便为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的任何字段与其他更抽象结构(如SetoidSemigroup的字段重叠, Monoid中的CommutativeMonoidBoundedJoinSemiLattice 重复。为了将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}},以恢复第二种方法的大部分便利。

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 isSemigroupCarrier),_≈_也确定了_∙_,所以我们甚至可以写:

id

实际上非常简洁。