Syntax as sets

迄今为止的一切都把公式留在它们所谈论的集合之外:公式是宿主层的数据,集合是结构的点,满足关系是二者之间的桥。第四部需要相反的方向。要在模型内部说某个集合可定义,或者用模型看得见的序去比较两条公式,公式自身就必须是集合。本章把它们注入进去。

这套编码刻意平淡。没有算术化,没有哥德尔编号,没有递归花招:公式的码是一个带标签的对,标签是构造子的序号,载荷是各部分的码。递归留在它该在的地方,即宿主的归纳类型 Formula 上,而码是一种边界格式。唯一的优雅之处是:集合常量本来就是集合,故常量即自身的码。

本章取作参数的,恰是编码所需的东西:一个带单射性的配对运算,以及自然数的一个单射。关于结构的其余一切都无关紧要,故本章是泛型的,层级稍后才来实例化它。

关于最要紧的那件交付物说一句。除码函数之外,还有一个归纳关系 Codes,读作「这个集合编码那条公式」,其构造子在子码的位置上携带子推导。关于码的推理走这个关系,而不走码之间的等式,理由是实际的:码值是深层嵌套的对,而两个码值之间的等式会迫使类型检查器把两边都展开。这个关系把形状变成构造子索引,于是匹配是句法的,而码值从不被归一化。

{-# OPTIONS --cubical --safe --guardedness #-}

open import Base.Prelude
open import Base.Truth
open import FOL.ZFStructure using ( ZFStructure )

module FOL.Coding {} (𝒮 : ZFStructure (hPropAlgebra ))
  (pr       : ZFStructure.S 𝒮  ZFStructure.S 𝒮  ZFStructure.S 𝒮)
  (pr-inj   :  {a b c d}  pr a b  pr c d  (a  c) × (b  d))
  (encℕ     :   ZFStructure.S 𝒮)
  (encℕ-inj :  {j k}  encℕ j  encℕ k  j  k)
  where

open ZFStructure 𝒮 using ( S )
open import FOL.Syntax
  using ( Term; con; var; Formula
        ; _∈̇_; _≐_; _∧̇_; _∨̇_; _⇒̇_; ¬̇_; ⊤̇; ⊥̇; ∃̇_; ∀̇_; ∀̇∈; ∃̇∈ )

import Cubical.Data.Empty as Empty
open import Cubical.Data.Nat using ( znots; snotz )
open import Cubical.Data.FinData using ( toℕ; inj-toℕ )

带标签的对

唯一的构造:构造子序号与载荷配成对。单射性直接来自那两个参数,而冲突模式把「两个不同构造子相比较」时反复出现的情形打包起来。

mkTag :   S  S
mkTag k x = pr (encℕ k) x

mkTag-inj :  {j k x y}  mkTag j x  mkTag k y  (j  k) × (x  y)
mkTag-inj p = encℕ-inj (pr-inj p .fst) , pr-inj p .snd

clash :  {j k x y} {A : Type }  (j  k  Empty.⊥)  mkTag j x  mkTag k y  A
clash ne p = Empty.rec (ne (mkTag-inj p .fst))

先看项,承诺的那点优雅在此出现:集合常量无须编码,因为它本来就是集合,只有变元的序号要被注入。项的分隔足够清楚,其单射性立得。

⌜_⌝ᵗ :  {n}  Term S n  S
 con x ⌝ᵗ = mkTag 0 x
 var i ⌝ᵗ = mkTag 1 (encℕ (toℕ i))

⌜⌝ᵗ-inj :  {n} (t u : Term S n)   t ⌝ᵗ   u ⌝ᵗ  t  u
⌜⌝ᵗ-inj (con x) (con y) p = cong con (mkTag-inj p .snd)
⌜⌝ᵗ-inj (con x) (var j) p = clash znots p
⌜⌝ᵗ-inj (var i) (con y) p = clash snotz p
⌜⌝ᵗ-inj (var i) (var j) p = cong var (inj-toℕ (encℕ-inj (mkTag-inj p .snd)))

然后是公式:十二个构造子,十二个标签。二元构造子把两个子码配成对,一元的直接取子码,而两个常量取一个虚载荷,因为标签已经把它们区分开了。

⌜_⌝ :  {n}  Formula S n  S
 t ∈̇ u    = mkTag 0  (pr  t ⌝ᵗ  u ⌝ᵗ)
 t  u    = mkTag 1  (pr  t ⌝ᵗ  u ⌝ᵗ)
 φ ∧̇ ψ    = mkTag 2  (pr  φ   ψ )
 φ ∨̇ ψ    = mkTag 3  (pr  φ   ψ )
 φ ⇒̇ ψ    = mkTag 4  (pr  φ   ψ )
 ¬̇ φ      = mkTag 5   φ 
 ⊤̇        = mkTag 6  (encℕ 0)
 ⊥̇        = mkTag 7  (encℕ 0)
 ∃̇ φ      = mkTag 8   φ 
 ∀̇ φ      = mkTag 9   φ 
 ∀̇∈ t φ   = mkTag 10 (pr  t ⌝ᵗ  φ )
 ∃̇∈ t φ   = mkTag 11 (pr  t ⌝ᵗ  φ )

编码关系

然后是本章真正的接口。Codes s φ 说集合 s 编码公式 φ,是一个索引归纳族,其构造子恰在码函数作递归调用之处携带子推导。它与码函数携带同样的信息,只是呈现方式使得证明可以在编码的形状上匹配,而不必对码作计算。

data CodesT {n : } : S  Term S n  Type  where
  c-con : (x : S)      CodesT (mkTag 0 x) (con x)
  c-var : (i : Fin n)  CodesT (mkTag 1 (encℕ (toℕ i))) (var i)

data Codes : {n : }  S  Formula S n  Type  where
  c-∈  :  {n s s'} {t u : Term S n}
        CodesT s t  CodesT s' u  Codes (mkTag 0 (pr s s')) (t ∈̇ u)
  c-≐  :  {n s s'} {t u : Term S n}
        CodesT s t  CodesT s' u  Codes (mkTag 1 (pr s s')) (t  u)
  c-∧  :  {n s s'} {φ ψ : Formula S n}
        Codes s φ  Codes s' ψ  Codes (mkTag 2 (pr s s')) (φ ∧̇ ψ)
  c-∨  :  {n s s'} {φ ψ : Formula S n}
        Codes s φ  Codes s' ψ  Codes (mkTag 3 (pr s s')) (φ ∨̇ ψ)
  c-⇒  :  {n s s'} {φ ψ : Formula S n}
        Codes s φ  Codes s' ψ  Codes (mkTag 4 (pr s s')) (φ ⇒̇ ψ)
  c-¬  :  {n s} {φ : Formula S n}
        Codes s φ  Codes (mkTag 5 s) (¬̇ φ)
  c-⊤  :  {n}  Codes {n} (mkTag 6 (encℕ 0)) ⊤̇
  c-⊥  :  {n}  Codes {n} (mkTag 7 (encℕ 0)) ⊥̇
  c-∃  :  {n s} {φ : Formula S (suc n)}
        Codes s φ  Codes (mkTag 8 s) (∃̇ φ)
  c-∀  :  {n s} {φ : Formula S (suc n)}
        Codes s φ  Codes (mkTag 9 s) (∀̇ φ)
  c-∀∈ :  {n s s'} {t : Term S n} {φ : Formula S (suc n)}
        CodesT s t  Codes s' φ  Codes (mkTag 10 (pr s s')) (∀̇∈ t φ)
  c-∃∈ :  {n s s'} {t : Term S n} {φ : Formula S (suc n)}
        CodesT s t  Codes s' φ  Codes (mkTag 11 (pr s s')) (∃̇∈ t φ)

两个事实把关系与函数系在一起。每条公式都被它自己的码所编码,故该关系在该有的地方都有居民;而公式的任何一个码都就是那条公式的码,故该关系并不比函数多出什么。两者都是一次结构递归,而后者是后续诸章倚重的:它把推导 (匹配起来廉价) 换成等式 (归一化起来昂贵),恰在等式终于被需要的那一点上。

codesT-complete :  {n} (t : Term S n)  CodesT  t ⌝ᵗ t
codesT-complete (con x) = c-con x
codesT-complete (var i) = c-var i

codes-complete :  {n} (φ : Formula S n)  Codes  φ  φ
codes-complete (t ∈̇ u)  = c-∈  (codesT-complete t) (codesT-complete u)
codes-complete (t  u)  = c-≐  (codesT-complete t) (codesT-complete u)
codes-complete (φ ∧̇ ψ)  = c-∧  (codes-complete φ)  (codes-complete ψ)
codes-complete (φ ∨̇ ψ)  = c-∨  (codes-complete φ)  (codes-complete ψ)
codes-complete (φ ⇒̇ ψ)  = c-⇒  (codes-complete φ)  (codes-complete ψ)
codes-complete (¬̇ φ)    = c-¬  (codes-complete φ)
codes-complete ⊤̇        = c-⊤
codes-complete ⊥̇        = c-⊥
codes-complete (∃̇ φ)    = c-∃  (codes-complete φ)
codes-complete (∀̇ φ)    = c-∀  (codes-complete φ)
codes-complete (∀̇∈ t φ) = c-∀∈ (codesT-complete t) (codes-complete φ)
codes-complete (∃̇∈ t φ) = c-∃∈ (codesT-complete t) (codes-complete φ)

codesT-canon :  {n s} {t : Term S n}  CodesT s t  s   t ⌝ᵗ
codesT-canon (c-con x) = refl
codesT-canon (c-var i) = refl

codes-canon :  {n s} {φ : Formula S n}  Codes s φ  s   φ 
codes-canon (c-∈ ct cu) =
  cong (mkTag 0)  (cong₂ pr (codesT-canon ct) (codesT-canon cu))
codes-canon (c-≐ ct cu) =
  cong (mkTag 1)  (cong₂ pr (codesT-canon ct) (codesT-canon cu))
codes-canon (c-∧ c d)   = cong (mkTag 2)  (cong₂ pr (codes-canon c) (codes-canon d))
codes-canon (c-∨ c d)   = cong (mkTag 3)  (cong₂ pr (codes-canon c) (codes-canon d))
codes-canon (c-⇒ c d)   = cong (mkTag 4)  (cong₂ pr (codes-canon c) (codes-canon d))
codes-canon (c-¬ c)     = cong (mkTag 5)  (codes-canon c)
codes-canon c-⊤         = refl
codes-canon c-⊥         = refl
codes-canon (c-∃ c)     = cong (mkTag 8)  (codes-canon c)
codes-canon (c-∀ c)     = cong (mkTag 9)  (codes-canon c)
codes-canon (c-∀∈ ct c) =
  cong (mkTag 10) (cong₂ pr (codesT-canon ct) (codes-canon c))
codes-canon (c-∃∈ ct c) =
  cong (mkTag 11) (cong₂ pr (codesT-canon ct) (codes-canon c))

典范性已经给出了「码决定公式」通常要陈述的内容:同一个码上的两份推导,迫使两条公式拥有相同的码;而一个手里握着推导、据以从码还原公式的论证,不再要求任何更多的东西。它们是绝大多数,而上面那个关系正是它们所围绕设计的接口。

但不是全部。有一个消费方要的是那条等式本身,理由是任何关系都答不了的,而本章最后一节把它证出来。当初把它挡在外面的那条反对意见是一个成本估计,而成本最终并不是那个估计所设想的样子。

小结

公式如今是集合了:⌜_⌝ 把构造子序号贴在各部分的码上,而常量编码自身。下游的接口是关系 Codes,它完备 (codes-complete) 且典范 (codes-canon),使码值不出现在类型检查器必须归一化的等式里。一切都对结构泛型,只需一个单射的配对与自然数的一个单射;层级把二者都供上。

码决定公式

同一元数、同一码的两条公式是同一条公式。这条陈述曾被丢掉,理由是它的自然证明是一张十二乘十二的网格,其中一百三十二条子句不含数学,而每个消费方当初都是围绕 Codes 关系设计的。如今来了一个要等式而非要关系的消费方,而它要的理由是任何关系都答不了的:递归的表是一个集合,故若两处不同子公式共用一个码,那张表就真的多值,垮掉的将是它的存在性,而不只是它的证明。

那张网格不必写。构造子可从标签还原,而标签是一个数,故一条公式的构造子是什么可以从标签出来:一个以标签为索引的类型族,说出「带那个标签」长什么样;一个函数把它造出来;而配对的单射性所给出的那条标签等式把后者搬到前者上。两者各十二条子句,再加十二条作情形分析,取代一百四十四条。

这与可构造性那一章「把十二个构造子对上八项要求」所用的是同一个动作,而它值得一般地说一次:当一次情形分析由两样东西索引、而某个标签已经把它们关联起来时,就从标签算出一侧,不要两侧都匹配。

tagOf :  {n}  Formula S n  
tagOf (t ∈̇ u)  = 0
tagOf (t  u)  = 1
tagOf (a ∧̇ b)  = 2
tagOf (a ∨̇ b)  = 3
tagOf (a ⇒̇ b)  = 4
tagOf (¬̇ a)    = 5
tagOf ⊤̇        = 6
tagOf ⊥̇        = 7
tagOf (∃̇ a)    = 8
tagOf (∀̇ a)    = 9
tagOf (∀̇∈ t a) = 10
tagOf (∃̇∈ t a) = 11

payOf :  {n}  Formula S n  S
payOf (t ∈̇ u)  = pr  t ⌝ᵗ  u ⌝ᵗ
payOf (t  u)  = pr  t ⌝ᵗ  u ⌝ᵗ
payOf (a ∧̇ b)  = pr  a   b 
payOf (a ∨̇ b)  = pr  a   b 
payOf (a ⇒̇ b)  = pr  a   b 
payOf (¬̇ a)    =  a 
payOf ⊤̇        = encℕ 0
payOf ⊥̇        = encℕ 0
payOf (∃̇ a)    =  a 
payOf (∀̇ a)    =  a 
payOf (∀̇∈ t a) = pr  t ⌝ᵗ  a 
payOf (∃̇∈ t a) = pr  t ⌝ᵗ  a 

shape :  {n} (φ : Formula S n)   φ   mkTag (tagOf φ) (payOf φ)
shape (t ∈̇ u)  = refl
shape (t  u)  = refl
shape (a ∧̇ b)  = refl
shape (a ∨̇ b)  = refl
shape (a ⇒̇ b)  = refl
shape (¬̇ a)    = refl
shape ⊤̇        = refl
shape ⊥̇        = refl
shape (∃̇ a)    = refl
shape (∀̇ a)    = refl
shape (∀̇∈ t a) = refl
shape (∃̇∈ t a) = refl

Match :  {n}    Formula S n  Type 
Match {n} 0  φ = Σ[ t  Term S n ] (Σ[ u  Term S n ] (φ  (t ∈̇ u)))
Match {n} 1  φ = Σ[ t  Term S n ] (Σ[ u  Term S n ] (φ  (t  u)))
Match {n} 2  φ = Σ[ a  Formula S n ] (Σ[ b  Formula S n ] (φ  (a ∧̇ b)))
Match {n} 3  φ = Σ[ a  Formula S n ] (Σ[ b  Formula S n ] (φ  (a ∨̇ b)))
Match {n} 4  φ = Σ[ a  Formula S n ] (Σ[ b  Formula S n ] (φ  (a ⇒̇ b)))
Match {n} 5  φ = Σ[ a  Formula S n ] (φ  (¬̇ a))
Match     6  φ = φ  ⊤̇
Match     7  φ = φ  ⊥̇
Match {n} 8  φ = Σ[ a  Formula S (suc n) ] (φ  (∃̇ a))
Match {n} 9  φ = Σ[ a  Formula S (suc n) ] (φ  (∀̇ a))
Match {n} 10 φ = Σ[ t  Term S n ] (Σ[ a  Formula S (suc n) ] (φ  ∀̇∈ t a))
Match {n} 11 φ = Σ[ t  Term S n ] (Σ[ a  Formula S (suc n) ] (φ  ∃̇∈ t a))
Match     _  _ = Empty.⊥*

matches :  {n} (φ : Formula S n)  Match (tagOf φ) φ
matches (t ∈̇ u)  = t , (u , refl)
matches (t  u)  = t , (u , refl)
matches (a ∧̇ b)  = a , (b , refl)
matches (a ∨̇ b)  = a , (b , refl)
matches (a ⇒̇ b)  = a , (b , refl)
matches (¬̇ a)    = a , refl
matches ⊤̇        = refl
matches ⊥̇        = refl
matches (∃̇ a)    = a , refl
matches (∀̇ a)    = a , refl
matches (∀̇∈ t a) = t , (a , refl)
matches (∃̇∈ t a) = t , (a , refl)

⌜⌝-inj :  {n} (φ ψ : Formula S n)   φ    ψ   φ  ψ

private
  go :  {n} (φ ψ : Formula S n)  Match (tagOf φ) ψ  payOf φ  payOf ψ  φ  ψ
  go (t ∈̇ u) ψ (t' , (u' , q)) p =
    cong₂ _∈̇_ (⌜⌝ᵗ-inj t t' (pr-inj (p  cong payOf q) .fst))
              (⌜⌝ᵗ-inj u u' (pr-inj (p  cong payOf q) .snd))  sym q
  go (t  u) ψ (t' , (u' , q)) p =
    cong₂ _≐_ (⌜⌝ᵗ-inj t t' (pr-inj (p  cong payOf q) .fst))
              (⌜⌝ᵗ-inj u u' (pr-inj (p  cong payOf q) .snd))  sym q
  go (a ∧̇ b) ψ (a' , (b' , q)) p =
    cong₂ _∧̇_ (⌜⌝-inj a a' (pr-inj (p  cong payOf q) .fst))
              (⌜⌝-inj b b' (pr-inj (p  cong payOf q) .snd))  sym q
  go (a ∨̇ b) ψ (a' , (b' , q)) p =
    cong₂ _∨̇_ (⌜⌝-inj a a' (pr-inj (p  cong payOf q) .fst))
              (⌜⌝-inj b b' (pr-inj (p  cong payOf q) .snd))  sym q
  go (a ⇒̇ b) ψ (a' , (b' , q)) p =
    cong₂ _⇒̇_ (⌜⌝-inj a a' (pr-inj (p  cong payOf q) .fst))
              (⌜⌝-inj b b' (pr-inj (p  cong payOf q) .snd))  sym q
  go (¬̇ a) ψ (a' , q) p = cong ¬̇_ (⌜⌝-inj a a' (p  cong payOf q))  sym q
  go ⊤̇ ψ q p = sym q
  go ⊥̇ ψ q p = sym q
  go (∃̇ a) ψ (a' , q) p = cong ∃̇_ (⌜⌝-inj a a' (p  cong payOf q))  sym q
  go (∀̇ a) ψ (a' , q) p = cong ∀̇_ (⌜⌝-inj a a' (p  cong payOf q))  sym q
  go (∀̇∈ t a) ψ (t' , (a' , q)) p =
    cong₂ ∀̇∈ (⌜⌝ᵗ-inj t t' (pr-inj (p  cong payOf q) .fst))
             (⌜⌝-inj a a' (pr-inj (p  cong payOf q) .snd))  sym q
  go (∃̇∈ t a) ψ (t' , (a' , q)) p =
    cong₂ ∃̇∈ (⌜⌝ᵗ-inj t t' (pr-inj (p  cong payOf q) .fst))
             (⌜⌝-inj a a' (pr-inj (p  cong payOf q) .snd))  sym q

⌜⌝-inj φ ψ e = go φ ψ
  (subst  k  Match k ψ) (sym (tp .fst)) (matches ψ)) (tp .snd)
  where
  tp = mkTag-inj (sym (shape φ)  e  shape ψ)