可以直接阅读本章,也可以通过交互式目录和依赖图选择其他路线。

交互式目录 · 依赖图

固定宇宙层级 ℓ,并假设 lem : LEM (ℓ-suc ℓ)。这个假设为相应层级的每个命题提供判定,并始终作为下文构造的显式参数。

module L.Coding.SlotClosure {ℓ : Level} (lem : LEM (ℓ-suc ℓ)) where

在 L 内部,每条公式都被编码为载体的元素,且每条公式都有一个键:以元数数码居前、公式编码居后的有序对。某个界处的槽位收集一条公式及其全部子公式的键,因此槽位是一棵键树,而界只是相关引理的接口参数。本章证明槽位是闭合的:凡复合公式的键位于槽位中,其直接子公式的键也位于其中;这对带子公式的七种构造子逐一成立。

抽象的闭合原理在这里落实到具体对象:由公式语法树生成的诸键满足 closedAt。这正是沿这些键递归定义图时所需的闭合前提。

语言的构造子中有七个带子公式,其余三个不带,对后者无可闭合。七条子句各是四个动作:把槽位的元素求逆回「它是谁的键」的那条公式、从子句的标签算出那条公式的构造子、把部件的键放回复合公式自己的槽位、再把它们提升到整体的槽位。

本章围绕三个数学对象展开。元数为 j 的公式 χ 的键 keyʟ χ 是数码 j 与 χ 的编码构成的有序对,而编码本身是编码模块的编码 LCode.⌜ χ ⌝。槽位 slot B φ 是由这类键组成的集合,即 φ 及其整棵子公式树的键。closedAt 则陈述:一个槽位对七个构造子闭合,即合取、析取、蕴含、两个量词与两个有界量词。

公式即对象语言的公式,其满足在可构造结构中读取:下文的满足符号一律指那里的满足。外围层级提供键与槽位所由构成的底层元素。

有序对编码键,且分量可恢复,因此一个键可以拆开为元数分量与编码分量。可构造结构承载诸编码;编码模块定义公式的编码 ⌜_⌝ 与码上的配对运算;闭包模块则逐形状陈述七条闭合子句及其引入形式。

满足表一章是三个核心对象的出处。它定义了公式的键 keyʟ、按构造子标签分解公式的形状引理 keyʟ-shape、挂在公式与某个界上的槽位 slot、把槽位元素求逆回其键所属公式的 slot-inv,以及关于键树的部件引理 Parts。

槽位元素只能在命题截断下求逆,因此每条子句都把该截断消去到一个命题。保持元数的二元情形以两个成员关系命题的合取为目标;一元情形与有界情形的目标则各是一个成员关系命题。

open import Cubical.HITs.CumulativeHierarchy.Base using ( V; _∈_ )
open import Cubical.HITs.CumulativeHierarchy.Constructions
  using ( module InfinitySet )

元数以数码记录,而抬升元数的构造子记录的是数码的后继;二者都取自外围的无穷集合。

open InfinitySet using ( #_; sucV )

可构造结构的载体是本章一切码、键与槽位所居的类型。

open hPropView 𝒮ʟ

这里的 Vec S n 表示长度为 n 的环境向量;重命名为 _⊨_ 的关系,则是在这种环境下读取限制可构造结构中的满足。

module AbsL = FOL.Absoluteness.Single 𝒮ᵥ isL isL-trans
open AbsL using () renaming ( _⊨ᵐ_ to _⊨_ )

把部件的键放回去

键树由部件引理支配。Parts.self 说公式自己的键在自己的槽位中;Parts.left、Parts.right 与 Parts.only 说复合公式的槽位包含其直接部件槽位中的键:这是沿构造子树的子树包含,由部件引理与求逆给出,从不来自界的任何序或向下的闭合。界 B 在其中只是作为接口参数传递。于是闭合子句所需做的,是把子句所读的对认作某个部件的键;下面两条引理恰完成此事。

module _ (B : S) where
private
  Sl : ∀ {n} → Formula S n → S
  Sl = slot B

键的计算,就元数为 j 的公式 χ 陈述:凡第一分量为元数数码 # j、第二分量为 ⌜ χ ⌝ 的编码分量的对,都等于键 (keyʟ χ) .fst。注意三个量互不相同:⌜ χ ⌝ 是公式的编码,其第一分量才是进入键的那个量,而键是数码居前的有序对。

key≡ : ∀ {j} (χ : Formula S j) (ar p : V ℓ) → # j ≡ ar
     → p ≡ (LCode.⌜ χ ⌝) .fst → pr ar p ≡ (keyʟ χ) .fst

两条假设都必须用到:元数等式与编码分量等式。证明是穿过两条计算法则的短链:j 的数码的第一分量是 # j,而编码对的第一分量是第一分量的对。

key≡ {j} χ ar p qa qp =
    cong₂ pr (sym qa) qp
  ∙ cong (λ w → pr w ((LCode.⌜ χ ⌝) .fst)) (sym (numeralL-fst j))
  ∙ sym (prʟ-fst (numeralL j) LCode.⌜ χ ⌝)

抬升形式对元数为 suc j 的公式 χ 陈述同一事实:此时键的第一分量是元数数码的后继,而这恰是构造子抬升元数时子句所读取的量。

keyS≡ : ∀ {j} (χ : Formula S (suc j)) (ar p : V ℓ) → # j ≡ ar
      → p ≡ (LCode.⌜ χ ⌝) .fst → pr (sucV ar) p ≡ (keyʟ χ) .fst

链是同一条,只是把后继沿元数等式推过去:suc j 的数码的第一分量是 suc (# j),而子句的抬升读法与之相合。

keyS≡ {j} χ ar p qa qp =
    cong₂ pr (cong sucV (sym qa)) qp
  ∙ cong (λ w → pr w ((LCode.⌜ χ ⌝) .fst)) (sym (numeralL-fst (suc j)))
  ∙ sym (prʟ-fst (numeralL (suc j)) LCode.⌜ χ ⌝)

七条子句

子句按各构造子所要求的闭合形状组织。共证明四个主体,每种形状一个:保持元数的二元构造子、保持元数的一元构造子、抬升元数的一元构造子、把项与抬升元数公式配对的二元构造子。同一主体下两条子句的不同只在构造子的标签与其交还的部件,而两者都是参数。每条子句经四个动作运行:把槽位元素求逆成一条公式、按标签读出其构造子、把部件的键放回该公式自己的槽位、再提升到整体的槽位。

module _ {n : ℕ} (φ : Formula S n) {k : ℕ} (γ : Vec S k) where
private
  δ : Vec S (suc (suc (suc k)))
  δ = B ∷ satTable B φ ∷ Sl φ ∷ γ

被证明闭合的递归以固定公式 φ 的槽位为索引,其环境携带三个具名条目:界、φ 处的满足表,以及该槽位。环境的其余槽位留给具体实例。

  Ci : Fin (suc (suc (suc k)))
  Ci = suc (suc zero)

位置 Ci 是槽位在此环境中的索引;每条子句都恰在这个位置读取槽位。

binSame : (k' : ℕ) (op : ∀ {m} → Formula S m → Formula S m → Formula S m)
        → (∀ {m} (ψ : Formula S m) → LCode.Match k' ψ
           → Σ[ a' ∶ Formula S m ] (Σ[ b' ∶ Formula S m ] (ψ ≡ op a' b')))
        → (∀ {m} (a' b' : Formula S m)
           → LCode.payOf (op a' b') ≡ prʟ LCode.⌜ a' ⌝ LCode.⌜ b' ⌝)

第一个主体覆盖保持元数的二元构造子,即合取、析取、蕴含的形状。其假设描述标签 k':一条公式与该标签匹配,当且仅当它由同元数的两条公式 op a' b' 构成,且这种复合的载荷是两个部件编码构成的有序对。

        → (∀ {m} (a' b' : Formula S m) (z : V ℓ)
           → ⟨ z ∈ (Sl a') .fst ⟩ → ⟨ z ∈ (Sl (op a' b')) .fst ⟩)
        → (∀ {m} (a' b' : Formula S m) (z : V ℓ)
           → ⟨ z ∈ (Sl b') .fst ⟩ → ⟨ z ∈ (Sl (op a' b')) .fst ⟩)
        → ⟨ δ ⊨ binShapeAt Ci k' (bothSameAt Ci) ⟩

两条闭合方向分别是部件引理给出的左、右子树包含;结论即子句自身:槽位对标签 k' 以「交还两个部件的键」的形状闭合。

binSame k' op get payOp inL inR = binSameClosed-in Ci k' δ
  (λ c ar a b c∈ sh → rec₁
    (isProp× ((pr (ar .fst) (a .fst) ∈ (Sl φ) .fst) .snd)
             ((pr (ar .fst) (b .fst) ∈ (Sl φ) .fst) .snd))
    (λ { (m , ψ , (q , incl)) →

第一步对槽位元素 c 求逆。它仅仅地是某条元数为 m 的公式 ψ 的键,求逆同时返回该键属于 φ 的槽位。目标是两个成员关系的合取,由 isProp× 是命题,这使截断消去合法。

      let r  = keyʟ-shape ψ k' (ar .fst) (pr (a .fst) (b .fst)) (sym q ∙ sh)
          g  = get ψ (r .fst)
          a' = g .fst
          b' = g .snd .fst
          eψ = g .snd .snd

第二步计算构造子。形状引理以子句的形状证明把 ψ 对上标签 k',随同匹配返回元数等式与载荷等式;分解假设进而把 ψ 写成两个直接子公式的 op a' b'。

          pay = sym (prʟ-fst LCode.⌜ a' ⌝ LCode.⌜ b' ⌝)
              ∙ cong (λ p → p .fst) (sym (payOp a' b'))
              ∙ cong (λ w → (LCode.payOf w) .fst) (sym eψ) ∙ r .snd .snd

第三步是关于载荷的共享计算。元素 c 是键形的对,其载荷分量记录着两个部件的编码分量;链条证明这些被记录的分量按分量分别是编码 ⌜ a' ⌝ 与 ⌜ b' ⌝:由构造子自身的载荷法则,ψ 的载荷是两个部件编码构成的对,而形状引理的载荷等式把它与从 c 读出的对连接起来。此处切莫混淆三个量:整条公式的编码 ⌜ ψ ⌝、位于其中的载荷分量,以及第一槽携带元数的最终的键。

          inψ : (χ : Formula S m) → ⟨ (keyʟ χ) .fst ∈ (Sl ψ) .fst ⟩
              → ⟨ (keyʟ χ) .fst ∈ (Sl φ) .fst ⟩
          inψ χ h = incl ((keyʟ χ) .fst) h
      in subst (λ w → ⟨ w ∈ (Sl φ) .fst ⟩)
           (sym (key≡ a' (ar .fst) (a .fst) (r .snd .fst) (sym (pr-inj pay .fst))))

第四个动作把键送回。先由辅助函数借求逆返回的包含,把任一同元数公式的键从其槽位提升到 φ 的槽位;再由形状引理的元数等式与编码单射性供给的第一分量等式喂给 key≡,把子句所读对的成员关系改写为 a' 的键的成员关系。

           (inψ a' (subst (λ w → ⟨ (keyʟ a') .fst ∈ (Sl w) .fst ⟩) (sym eψ)
             (inL a' b' _ (Parts.self B keyʟ a'))))
       , subst (λ w → ⟨ w ∈ (Sl φ) .fst ⟩)
           (sym (key≡ b' (ar .fst) (b .fst) (r .snd .fst) (sym (pr-inj pay .snd))))

右分量以右侧闭合方向与单射性供给的第二分量等式重复同一组装,改写为含 b' 那个对的成员关系。两半合拢,子句证毕。

           (inψ b' (subst (λ w → ⟨ (keyʟ b') .fst ∈ (Sl w) .fst ⟩) (sym eψ)
             (inR a' b' _ (Parts.self B keyʟ b')))) })
    (slot-inv B φ (c .fst) c∈))

消去由求逆供给,那条包含正是从那里来的。两侧分量组装完毕,子句即告证成。

andC : ⟨ δ ⊨ binShapeAt Ci 2 (bothSameAt Ci) ⟩
andC = binSame 2 _∧̇_ (λ _ m → m) (λ _ _ → refl)
         (λ a' b' → Parts.left B keyʟ (a' ∧̇ b') a' b')
         (λ a' b' → Parts.right B keyʟ (a' ∧̇ b') a' b')

合取是第一个实例:a' ∧̇ b' 的槽位包含两个合取支槽位的键。

orC : ⟨ δ ⊨ binShapeAt Ci 3 (bothSameAt Ci) ⟩
orC = binSame 3 _∨̇_ (λ _ m → m) (λ _ _ → refl)
        (λ a' b' → Parts.left B keyʟ (a' ∨̇ b') a' b')
        (λ a' b' → Parts.right B keyʟ (a' ∨̇ b') a' b')

析取是形状相同的第二个实例,带自己的标签与自己的部件引理。

impC : ⟨ δ ⊨ binShapeAt Ci 4 (bothSameAt Ci) ⟩
impC = binSame 4 _⇒̇_ (λ _ m → m) (λ _ _ → refl)
         (λ a' b' → Parts.left B keyʟ (a' ⇒̇ b') a' b')
         (λ a' b' → Parts.right B keyʟ (a' ⇒̇ b') a' b')

蕴含是第三个:a' ⇒̇ b' 的槽位包含前件槽位的键与后件槽位的键,子句由同样四个动作证得。

unSame : (k' : ℕ) (op : ∀ {m} → Formula S m → Formula S m)
       → (∀ {m} (ψ : Formula S m) → LCode.Match k' ψ
          → Σ[ a' ∶ Formula S m ] (ψ ≡ op a'))
       → (∀ {m} (a' : Formula S m) → LCode.payOf (op a') ≡ LCode.⌜ a' ⌝)

辅助引理 unSame 证明了一个假想的、保持元数的一元运算所满足的相应闭合原理。本语言的十个构造子都不具有这种形状,因此 closedAt 不使用这一辅助引理。

       → (∀ {m} (a' : Formula S m) (z : V ℓ)
          → ⟨ z ∈ (Sl a') .fst ⟩ → ⟨ z ∈ (Sl (op a')) .fst ⟩)
       → ⟨ δ ⊨ unShapeAt Ci k' (oneSameAt Ci) ⟩

闭合方向与结论都是单部件形状:被要求交还的只有那唯一子公式的键。

unSame k' op get payOp inA = unSameClosed-in Ci k' δ
  (λ c ar a c∈ sh → rec₁ ((pr (ar .fst) (a .fst) ∈ (Sl φ) .fst) .snd)
    (λ { (m , ψ , (q , incl)) →
      let r  = keyʟ-shape ψ k' (ar .fst) (a .fst) (sym q ∙ sh)
          g  = get ψ (r .fst)

证明以一个分量运行同样的四个动作。求逆产出 ψ 及其到 φ 槽位的包含,形状引理按标签分解它,而此处的读取只涉及元数与那唯一的编码。

          a' = g .fst
          eψ = g .snd
          pay = cong (λ p → p .fst) (sym (payOp a'))
              ∙ cong (λ w → (LCode.payOf w) .fst) (sym eψ) ∙ r .snd .snd
      in subst (λ w → ⟨ w ∈ (Sl φ) .fst ⟩)

这里的共享计算更短:op a' 的载荷就是 a' 自己的编码,故链条把元素 c 中记录的编码分量直接认同为 a' 的编码分量,无须拆分任何对。

           (sym (key≡ a' (ar .fst) (a .fst) (r .snd .fst) (sym pay)))
           (incl ((keyʟ a') .fst)
             (subst (λ w → ⟨ (keyʟ a') .fst ∈ (Sl w) .fst ⟩) (sym eψ)
               (inA a' _ (Parts.self B keyʟ a')))) })
    (slot-inv B φ (c .fst) c∈))

第四个动作一次性组装该情形:a' 的键在自己的槽位中,inA 把它移入 op a' 的槽位,incl 提升到 φ 的槽位,key≡ 完成改写,元数等式包含在内。

unSucc : (k' : ℕ) (op : ∀ {m} → Formula S (suc m) → Formula S m)
       → (∀ {m} (ψ : Formula S m) → LCode.Match k' ψ
          → Σ[ a' ∶ Formula S (suc m) ] (ψ ≡ op a'))
       → (∀ {m} (a' : Formula S (suc m)) → LCode.payOf (op a') ≡ LCode.⌜ a' ⌝)
       → (∀ {m} (a' : Formula S (suc m)) (z : V ℓ)

第三个主体覆盖抬升元数的一元构造子,即两个量词的形状。其假设是抬升版本:标签恰与由抬升元数公式经 op 构成的公式匹配,载荷就是该公式自己的编码,且唯一一条闭合方向把它的键送入复合槽位。

          → ⟨ z ∈ (Sl a') .fst ⟩ → ⟨ z ∈ (Sl (op a')) .fst ⟩)
       → ⟨ δ ⊨ unShapeAt Ci k' (oneSuccAt Ci) ⟩

读取与结论使用抬升形式:子句读取的对以元数数码的后继居首,并要求子公式的键以单部件形状交还。

unSucc k' op get payOp inA = unSuccClosed-in Ci k' δ
  (λ c ar a c∈ sh → rec₁ ((pr (sucV (ar .fst)) (a .fst) ∈ (Sl φ) .fst) .snd)
    (λ { (m , ψ , (q , incl)) →
      let r  = keyʟ-shape ψ k' (ar .fst) (a .fst) (sym q ∙ sh)
          g  = get ψ (r .fst)

前两个动作照旧:把元素求逆成公式,再按标签分解出唯一的、元数抬升的子公式。

          a' = g .fst
          eψ = g .snd
          pay = cong (λ p → p .fst) (sym (payOp a'))
              ∙ cong (λ w → (LCode.payOf w) .fst) (sym eψ) ∙ r .snd .snd
      in subst (λ w → ⟨ w ∈ (Sl φ) .fst ⟩)

载荷计算把被记录的编码分量认同为子公式的编码。另一方面,形状等式给出 # m ≡ ar .fst;只有 keyS≡ 对该等式施加 sucV 时,后继才被引入。

           (sym (keyS≡ a' (ar .fst) (a .fst) (r .snd .fst) (sym pay)))
           (incl ((keyʟ a') .fst)
             (subst (λ w → ⟨ (keyʟ a') .fst ∈ (Sl w) .fst ⟩) (sym eψ)
               (inA a' _ (Parts.self B keyʟ a')))) })
    (slot-inv B φ (c .fst) c∈))

改写经由 keyS≡ 完成:它对 # m ≡ ar .fst 施加 sucV,再与编码分量等式合并,从而把子句所读的对认同为 a' 的键。

binSucc : (k' : ℕ)
        → (op : ∀ {m} → Term S m → Formula S (suc m) → Formula S m)
        → (∀ {m} (ψ : Formula S m) → LCode.Match k' ψ
           → Σ[ t ∶ Term S m ] (Σ[ a' ∶ Formula S (suc m) ] (ψ ≡ op t a')))
        → (∀ {m} (t : Term S m) (a' : Formula S (suc m))

第四个主体覆盖有界量词,其构造子把一个项与一个元数抬升的公式配对。这种复合的载荷按次序编码项与子公式,而只有子公式本身是公式,故被要求交回的只有它的键。

           → LCode.payOf (op t a') ≡ prʟ LCode.⌜ t ⌝ᵗ LCode.⌜ a' ⌝)
        → (∀ {m} (t : Term S m) (a' : Formula S (suc m)) (z : V ℓ)
           → ⟨ z ∈ (Sl a') .fst ⟩ → ⟨ z ∈ (Sl (op t a')) .fst ⟩)
        → ⟨ δ ⊨ binShapeAt Ci k' (succSndAt Ci) ⟩

结论是第二分量形状:子句读取的对以抬升的元数居前、公式的编码分量居后,并要求交回子公式的键。

binSucc k' op get payOp inA = binSuccClosed-in Ci k' δ
  (λ c ar a b c∈ sh → rec₁ ((pr (sucV (ar .fst)) (b .fst) ∈ (Sl φ) .fst) .snd)
    (λ { (m , ψ , (q , incl)) →
      let r  = keyʟ-shape ψ k' (ar .fst) (pr (a .fst) (b .fst)) (sym q ∙ sh)
          g  = get ψ (r .fst)

元素 c 是键形的对,其载荷携带两个分量:居前的项 t 的编码分量,与居后的子公式 a' 的编码分量。子句读取的是抬升元数连同第二分量。

          t  = g .fst
          a' = g .snd .fst
          eψ = g .snd .snd
          pay = sym (prʟ-fst LCode.⌜ t ⌝ᵗ LCode.⌜ a' ⌝)
              ∙ cong (λ p → p .fst) (sym (payOp t a'))

共享计算把元素中记录的载荷按分量认同为 ⌜ t ⌝ᵗ 与 ⌜ a' ⌝ 的编码对。改写所消耗的是由编码单射性供给的第二分量等式;项乘坐在第一分量里,随后退出。

              ∙ cong (λ w → (LCode.payOf w) .fst) (sym eψ) ∙ r .snd .snd
      in subst (λ w → ⟨ w ∈ (Sl φ) .fst ⟩)
           (sym (keyS≡ a' (ar .fst) (b .fst) (r .snd .fst)
             (sym (pr-inj pay .snd))))
           (incl ((keyʟ a') .fst)

keyS≡ 的改写使用元数等式与第二分量等式,落定为子句所读对的成员关系。第四个动作随后把 a' 的键经它自己的槽位与闭合方向提升到 φ 的槽位,而求逆供给那条包含。

             (subst (λ w → ⟨ (keyʟ a') .fst ∈ (Sl w) .fst ⟩) (sym eψ)
               (inA t a' _ (Parts.self B keyʟ a')))) })
    (slot-inv B φ (c .fst) c∈))

两个量词例示第三个主体。各自供给标签、分解、定义性成立的载荷等式,以及唯一一条闭合方向:∃̇ a' 的槽位包含 a' 槽位的键,全称量词亦然。

exC : ⟨ δ ⊨ unShapeAt Ci 6 (oneSuccAt Ci) ⟩
exC = unSucc 6 ∃̇_ (λ _ m → m) (λ _ → refl)
        (λ a' → Parts.only B keyʟ (∃̇ a') a')

全称量词是同一主体的第二个实例,带标签七与自己的部件引理。

allC : ⟨ δ ⊨ unShapeAt Ci 7 (oneSuccAt Ci) ⟩
allC = unSucc 7 ∀̇_ (λ _ m → m) (λ _ → refl)
         (λ a' → Parts.only B keyʟ (∀̇ a') a')

两个有界量词以标签八与九例示第四个主体:∀̇∈ t a' 的槽位包含 a' 槽位的键,存在有界量词亦然。

allInC : ⟨ δ ⊨ binShapeAt Ci 8 (succSndAt Ci) ⟩
allInC = binSucc 8 ∀̇∈ (λ _ m → m) (λ _ _ → refl)
           (λ t a' → Parts.only B keyʟ (∀̇∈ t a') a')

存在有界量词是七条子句中的最后一条。

exInC : ⟨ δ ⊨ binShapeAt Ci 9 (succSndAt Ci) ⟩
exInC = binSucc 9 ∃̇∈ (λ _ m → m) (λ _ _ → refl)
          (λ t a' → Parts.only B keyʟ (∃̇∈ t a') a')

七条子句组装成闭合陈述 closedAt:φ 在该界处的槽位对每个带子公式的构造子闭合。这就消解了对编码的递归对其索引集陈述的假设;也正因如此,这样的递归才能在每个复合码处求助于直接子公式键处记录的取值。本章的三个对象各尽其用:键识别了子句所读的对,槽位树供给了闭合方向,closedAt 汇集了结果。

slotClosed : ⟨ δ ⊨ closedAt Ci ⟩
slotClosed = andC , (orC , (impC
           , (exC , (allC , (allInC , exInC)))))