module Cat.Displayed.Doctrine.Logic

The internal logic of a regular hyperdoctrineπŸ”—

Fix a regular hyperdoctrine over a finitely complete category We’ve claimed that the data of is enough to interpret regular logic relative to but that proof was deferred. This module actually finishes this claim, but because syntax is always fiddly, it’s quite long.

TermsπŸ”—

The first thing we’ll do is strictify a bit. Instead of working directly with arbitrary morphisms, it’s slightly better β€” for the computational behaviour of substitution β€” to have a syntactic presentation of the terms of our logic. We start with the types, built inductively using _`Γ—_, and with an injection from the objects of We also single out a class of objects which are built from repeated pairing onto the terminal object to be the contexts.

data Ty : Typeω where
  ↑    : Ob β†’ Ty
  _`Γ—_ : Ty β†’ Ty β†’ Ty

data Cx : Typeω where
  []  : Cx
  _Κ»_ : Cx β†’ Ty β†’ Cx

These two classes can be interpreted into objects in the base category. Throughout this entire module, we’ll write for the semantic interpretation of a syntactic object; In the formalisation, the brackets are always superscript with an indicator of what is being interpreted.

⟦_βŸ§α΅— : Ty β†’ Ob
⟦ ↑ x    βŸ§α΅— = x
⟦ t `Γ— s βŸ§α΅— = ⟦ t βŸ§α΅— βŠ—β‚€ ⟦ s βŸ§α΅—

⟦_⟧ᢜ : Cx β†’ Ob
⟦ []    ⟧ᢜ = Terminal.top terminal
⟦ Ξ“ Κ» x ⟧ᢜ = ⟦ Ξ“ ⟧ᢜ βŠ—β‚€ ⟦ x βŸ§α΅—

Next we have the syntax of variables: A variable encodes a projection which, because of the structure of our contexts, is always of the form Put syntactically, we can always access the latest variable, and we can also weaken variables by one.

data _βˆ‹_ : Cx β†’ Ty β†’ TypeΟ‰ where
  stop : (Ξ“ Κ» Ο„) βˆ‹ Ο„
  pop  : Ξ“ βˆ‹ Ο„ β†’ (Ξ“ Κ» Οƒ) βˆ‹ Ο„

Finally, we can define the class of terms: A term is a variable, or lies in the fragment dealing with Cartesian products, or comes from applying a function from the base category to an argument this is required if we want our logic to be applicable to more than variables and products!

data Tm : Cx → Ty → Typeω where
  var : Ξ“ βˆ‹ Ο„ β†’ Tm Ξ“ Ο„

  _,_ : Tm Ξ“ Ο„ β†’ Tm Ξ“ Οƒ β†’ Tm Ξ“ (Ο„ `Γ— Οƒ)
  `π₁ : Tm Ξ“ (Ο„ `Γ— Οƒ) β†’ Tm Ξ“ Ο„
  `Ο€β‚‚ : Tm Ξ“ (Ο„ `Γ— Οƒ) β†’ Tm Ξ“ Οƒ

  fun : Hom ⟦ Ο„ βŸ§α΅— ⟦ Οƒ βŸ§α΅— β†’ Tm Ξ“ Ο„ β†’ Tm Ξ“ Οƒ

-- Superscript n for "name", e for "expression"
⟦_⟧ⁿ : Ξ“ βˆ‹ Ο„ β†’ Hom ⟦ Ξ“ ⟧ᢜ ⟦ Ο„ βŸ§α΅—
⟦ stop ⟧ⁿ  = Ο€β‚‚
⟦ pop x ⟧ⁿ = ⟦ x ⟧ⁿ ∘ π₁

⟦_βŸ§α΅‰ : Tm Ξ“ Ο„ β†’ Hom ⟦ Ξ“ ⟧ᢜ ⟦ Ο„ βŸ§α΅—
⟦ var x βŸ§α΅‰   = ⟦ x ⟧ⁿ
⟦ x , y βŸ§α΅‰   = ⟨ ⟦ x βŸ§α΅‰ , ⟦ y βŸ§α΅‰ ⟩
⟦ `π₁ x βŸ§α΅‰   = π₁ ∘ ⟦ x βŸ§α΅‰
⟦ `Ο€β‚‚ x βŸ§α΅‰   = Ο€β‚‚ ∘ ⟦ x βŸ§α΅‰
⟦ fun f t βŸ§α΅‰ = f ∘ ⟦ t βŸ§α΅‰

Renamings and substitutionsπŸ”—

Even after we have a good grasp on the morphisms in that we want to call terms, there are still two classes of maps, now between pairs of contexts, that we must single out. A renaming maps variables in to variables in there’s an identity renaming, and you can either choose to keep or drop variables from

If we allow ourselves to map variables in to terms in we get the class of substitutions. Now, instead of keeping a variable as-is, we can instead use an arbitrary term:

data Ren : Cx → Cx → Typeω where
  stop : Ren Ξ“ Ξ“
  drop : Ren Ξ“ Ξ” β†’ Ren (Ξ“ Κ» Ο„) Ξ”
  keep : Ren Ξ“ Ξ” β†’ Ren (Ξ“ Κ» Ο„) (Ξ” Κ» Ο„)

data Sub : Cx → Cx → Typeω where
  stop : Sub Ξ“ Ξ“
  _,_  : Tm Ξ“ Ο„  β†’ Sub Ξ“ Ξ” β†’ Sub Ξ“ (Ξ” Κ» Ο„)
  drop : Sub Ξ“ Ξ” β†’ Sub (Ξ“ Κ» Ο„) Ξ”
-- Superscript r for "renaming", s for "substitution"
⟦_⟧ʳ : Ren Ξ“ Ξ” β†’ Hom ⟦ Ξ“ ⟧ᢜ ⟦ Ξ” ⟧ᢜ
⟦ stop ⟧ʳ = id
⟦ drop r ⟧ʳ = ⟦ r ⟧ʳ ∘ π₁
⟦ keep r ⟧ʳ = ⟦ r ⟧ʳ βŠ—β‚ id

⟦_⟧˒ : Sub Ξ“ Ξ” β†’ Hom ⟦ Ξ“ ⟧ᢜ ⟦ Ξ” ⟧ᢜ
⟦ stop    ⟧˒ = id
⟦ x , s   ⟧˒ = ⟨ ⟦ s ⟧˒ , ⟦ x βŸ§α΅‰ ⟩
⟦ drop ρ  ⟧˒ = ⟦ ρ ⟧˒ ∘ π₁

One substitution which deserves special attention is the β€œterminating” substitution, which maps from an arbitrary to the empty context.

!Λ’ : βˆ€ {Ξ“} β†’ Sub Ξ“ []
!Λ’ {[]}    = Sub.stop
!Λ’ {Ξ“ Κ» x} = Sub.drop !Λ’

Now comes the fiddly part of dealing with syntax: the endless recursive functions for the action of renamings and substitutions on everything, and all the correctness lemmas that guarantee we’re doing the right thing.

_∘ʳ_    : Ren Ξ“ Ξ” β†’ Ren Ξ” Θ β†’ Ren Ξ“ Θ
ren-var : Ren Ξ“ Ξ” β†’ Ξ” βˆ‹ Ο„ β†’ Ξ“ βˆ‹ Ο„
ren-tm  : Ren Ξ“ Ξ” β†’ Tm Ξ” Ο„ β†’ Tm Ξ“ Ο„
sub-var : Sub Ξ“ Ξ” β†’ Ξ” βˆ‹ Ο„ β†’ Tm Ξ“ Ο„
sub-tm  : Sub Ξ“ Ξ” β†’ Tm Ξ” Ο„ β†’ Tm Ξ“ Ο„

ren-var-correct
  : (ρ : Ren Ξ“ Ξ”) (v : Ξ” βˆ‹ Ο„) β†’ ⟦ ren-var ρ v ⟧ⁿ ≑ ⟦ v ⟧ⁿ ∘ ⟦ ρ ⟧ʳ
sub-var-correct
  : (ρ : Sub Ξ“ Ξ”) (t : Ξ” βˆ‹ Ο„) β†’ ⟦ sub-var ρ t βŸ§α΅‰ ≑ ⟦ t ⟧ⁿ ∘ ⟦ ρ ⟧˒

ren-tm-correct
  : (ρ : Ren Ξ“ Ξ”) (v : Tm Ξ” Ο„) β†’ ⟦ ren-tm ρ v βŸ§α΅‰ ≑ ⟦ v βŸ§α΅‰ ∘ ⟦ ρ ⟧ʳ
sub-tm-correct
  : (ρ : Sub Ξ“ Ξ”) (t : Tm Ξ” Ο„) β†’ ⟦ sub-tm ρ t βŸ§α΅‰ ≑ ⟦ t βŸ§α΅‰ ∘ ⟦ ρ ⟧˒
For the sake of the reader’s sanity, we’ve chosen to display only the type signatures, and all the implementations are in this <details> block.
stop   ∘ʳ ρ     = ρ
drop Οƒ ∘ʳ ρ     = drop (Οƒ ∘ʳ ρ)

keep Οƒ ∘ʳ stop   = keep Οƒ
keep Οƒ ∘ʳ drop ρ = drop (Οƒ ∘ʳ ρ)
keep Οƒ ∘ʳ keep ρ = keep (Οƒ ∘ʳ ρ)

ren-var stop     v       = v
ren-var (drop Οƒ) v       = pop (ren-var Οƒ v)
ren-var (keep Οƒ) stop    = stop
ren-var (keep Οƒ) (pop v) = pop (ren-var Οƒ v)

ren-tm ρ (var x)    = var (ren-var ρ x)
ren-tm ρ (t , s)    = ren-tm ρ t , ren-tm ρ s
ren-tm ρ (`π₁ t)    = `π₁ (ren-tm ρ t)
ren-tm ρ (`Ο€β‚‚ t)    = `Ο€β‚‚ (ren-tm ρ t)
ren-tm ρ (fun x t)  = fun x (ren-tm ρ t)

sub-var stop v          = var v
sub-var (x , ρ) stop    = x
sub-var (x , ρ) (pop v) = sub-var ρ v
sub-var (drop ρ) v      = ren-tm (drop stop) (sub-var ρ v)

sub-tm ρ (var x)    = sub-var ρ x
sub-tm ρ (t , s)    = sub-tm ρ t , sub-tm ρ s
sub-tm ρ (`π₁ tm)   = `π₁ (sub-tm ρ tm)
sub-tm ρ (`Ο€β‚‚ tm)   = `Ο€β‚‚ (sub-tm ρ tm)
sub-tm ρ (fun x tm) = fun x (sub-tm ρ tm)

ren-var-correct stop v = sym (idr _)
ren-var-correct (drop ρ) v = ap (_∘ π₁) (ren-var-correct ρ v) βˆ™ sym (assoc _ _ _)
ren-var-correct (keep ρ) stop    = sym (Ο€β‚‚βˆ˜βŸ¨βŸ© βˆ™ idl _)
ren-var-correct (keep ρ) (pop v) =
  pushl (ren-var-correct ρ v) βˆ™ sym (pullr Ο€β‚βˆ˜βŸ¨βŸ©)

ren-tm-correct ρ (var x)   = ren-var-correct ρ x
ren-tm-correct ρ (v , v₁)  = sym (⟨⟩∘ _ βˆ™ sym (apβ‚‚ ⟨_,_⟩ (ren-tm-correct ρ v) (ren-tm-correct ρ v₁)))
ren-tm-correct ρ (`π₁ v)   = pushr (ren-tm-correct ρ v)
ren-tm-correct ρ (`Ο€β‚‚ v)   = pushr (ren-tm-correct ρ v)
ren-tm-correct ρ (fun x v) = pushr (ren-tm-correct ρ v)

sub-var-correct stop    t       = sym (idr _)
sub-var-correct (x , ρ) stop    = sym Ο€β‚‚βˆ˜βŸ¨βŸ©
sub-var-correct (x , ρ) (pop t) = sym (pullr Ο€β‚βˆ˜βŸ¨βŸ© βˆ™ sym (sub-var-correct ρ t))
sub-var-correct (drop ρ) v      =
  ren-tm-correct (drop stop) (sub-var ρ v)
    βˆ™ extendl (idr _ βˆ™ sub-var-correct ρ v)

sub-tm-correct ρ (var x) = sub-var-correct ρ x
sub-tm-correct ρ (t , s) =
  sym (⟨⟩∘ _ βˆ™ apβ‚‚ ⟨_,_⟩ (sym (sub-tm-correct ρ t)) (sym (sub-tm-correct ρ s)))
sub-tm-correct ρ (`π₁ t) = ap (π₁ ∘_) (sub-tm-correct ρ t) βˆ™ assoc _ _ _
sub-tm-correct ρ (`Ο€β‚‚ t) = ap (Ο€β‚‚ ∘_) (sub-tm-correct ρ t) βˆ™ assoc _ _ _
sub-tm-correct ρ (fun x t) = ap (x ∘_) (sub-tm-correct ρ t) βˆ™ assoc _ _ _

FormulaeπŸ”—

Finally, we have a syntactic description of the objects in that concern regular logic: they are built from the following grammar. Each fibre has a top element and conjunction. We single out the existential quantification along the latest variable

Moreover, we can define equality of terms in terms of existential quantification, though not along a variable, so it gets its own constructor; and finally, if you have a predicate on a type and a term then you can from

data Formula : Cx → Typeω where
  `⊀   : Formula Ξ“
  _`∧_ : Formula Ξ“ β†’ Formula Ξ“ β†’ Formula Ξ“

  `βˆƒ   : Formula (Ξ“ Κ» Ο„) β†’ Formula Ξ“

  _=α΅–_ : Tm Ξ“ Ο„ β†’ Tm Ξ“ Ο„ β†’ Formula Ξ“

  pred : β„™.Ob[ ⟦ Ο„ βŸ§α΅— ] β†’ Tm Ξ“ Ο„ β†’ Formula Ξ“

The formulas over become objects over in the way that was implied in the definition of hyperdoctrine: in particular, the inclusion of semantic predicates is interpreted into substitution, and the equality predicate is interpreted by the formula

⟦_βŸ§α΅– : Formula Ξ“ β†’ β„™.Ob[ ⟦ Ξ“ ⟧ᢜ ]
⟦ x `∧ y    βŸ§α΅– = ⟦ x βŸ§α΅– & ⟦ y βŸ§α΅–
⟦ `⊀        βŸ§α΅– = aye
⟦ `βˆƒ x      βŸ§α΅– = exists π₁ ⟦ x βŸ§α΅–
⟦ pred p tm βŸ§α΅– = p [ ⟦ tm βŸ§α΅‰ ]
⟦ t =α΅– s    βŸ§α΅– = exists ⟨ id , id ⟩ aye [ ⟨ ⟦ t βŸ§α΅‰ , ⟦ s βŸ§α΅‰ ⟩ ]

Once again we have the strictified presentation of substitution, though this time it’s short enough to present inline:

sub-prop : Sub Ξ“ Ξ” β†’ Formula Ξ” β†’ Formula Ξ“
sub-prop ρ (Ο† `∧ ψ)   = sub-prop ρ Ο† `∧ sub-prop ρ ψ
sub-prop ρ (t =α΅– s)   = sub-tm ρ t =α΅– sub-tm ρ s
sub-prop ρ `⊀         = `⊀
sub-prop ρ (`βˆƒ Ο†)     = `βˆƒ (sub-prop (var stop , Sub.drop ρ) Ο†)
sub-prop ρ (pred x t) = pred x (sub-tm ρ t)

Unfortunately, we also have to prove that this is sent by to the semantic substitution:

sub-prop-correct
  : (ρ : Sub Ξ“ Ξ”) (Ο† : Formula Ξ”)
  β†’ ⟦ sub-prop ρ Ο† βŸ§α΅– ≑ ⟦ Ο† βŸ§α΅– [ ⟦ ρ ⟧˒ ]

sub-prop-correct ρ (Ο† `∧ ψ) = sym $
  (⟦ Ο† βŸ§α΅– & ⟦ ψ βŸ§α΅–) [ ⟦ ρ ⟧˒ ]           β‰‘βŸ¨ subst-& _ _ _ βŸ©β‰‘
  ⟦ Ο† βŸ§α΅– [ ⟦ ρ ⟧˒ ] & ⟦ ψ βŸ§α΅– [ ⟦ ρ ⟧˒ ]  β‰‘Λ˜βŸ¨ apβ‚‚ _&_ (sub-prop-correct ρ Ο†) (sub-prop-correct ρ ψ) βŸ©β‰‘Λ˜
  ⟦ sub-prop ρ (Ο† `∧ ψ) βŸ§α΅–               ∎

sub-prop-correct ρ `⊀       = sym (subst-aye _)

sub-prop-correct {Ξ“ = Ξ“} ρ (`βˆƒ {Ξ“ = Ξ”} {Ο„ = Ο„} Ο†) =
  exists π₁ ⟦ sub-prop (var stop , drop ρ) Ο† βŸ§α΅–   β‰‘βŸ¨ ap (exists _) (sub-prop-correct _ Ο†) βŸ©β‰‘
  exists π₁ (⟦ Ο† βŸ§α΅– [ ⟨ ⟦ ρ ⟧˒ ∘ π₁ , Ο€β‚‚ ⟩ ])     β‰‘βŸ¨ beck-chevalley rem₁ βŸ©β‰‘
  (exists π₁ ⟦ Ο† βŸ§α΅–) [ ⟦ ρ ⟧˒ ]                   ∎
  where

The most interesting case is the one above, for existential quantification, where we use Beck-Chevalley applied to the pullback square

to commute

sub-prop-correct ρ (pred P t) =
  apβ‚‚ _[_] refl (sub-tm-correct ρ t) βˆ™ sym (subst-∘ _ _)

sub-prop-correct ρ (s =α΅– t) =
  exists ⟨ id , id ⟩ aye [ ⟨ ⟦ sub-tm ρ s βŸ§α΅‰ , ⟦ sub-tm ρ t βŸ§α΅‰ ⟩ ]
    β‰‘βŸ¨ apβ‚‚ _[_] refl (apβ‚‚ ⟨_,_⟩ (sub-tm-correct ρ s) (sub-tm-correct ρ t)) βŸ©β‰‘
  exists ⟨ id , id ⟩ aye [ ⟨ ⟦ s βŸ§α΅‰ ∘ ⟦ ρ ⟧˒ , ⟦ t βŸ§α΅‰ ∘ ⟦ ρ ⟧˒ ⟩ ]
    β‰‘βŸ¨ apβ‚‚ _[_] refl (sym (⟨⟩∘ _)) βŸ©β‰‘
  exists ⟨ id , id ⟩ aye [ ⟨ ⟦ s βŸ§α΅‰ , ⟦ t βŸ§α΅‰ ⟩ ∘ ⟦ ρ ⟧˒ ]
    β‰‘βŸ¨ sym (subst-∘ _ _) βŸ©β‰‘
  (exists ⟨ id , id ⟩ aye [ ⟨ ⟦ s βŸ§α΅‰ , ⟦ t βŸ§α΅‰ ⟩ ]) [ ⟦ ρ ⟧˒ ]
    ∎

A sequent calculusπŸ”—

We now turn to the problem of proving that our interpretation above satisfies the rules of regular logic. We will start by defining the entailment relation between syntactic formulae which is equivalent to entails iff thinks that the interpretation of implies that of

infix 10 _⊨_

opaque
  _⊨_ : βˆ€ {Ξ“} β†’ (Ο† ψ : Formula Ξ“) β†’ Type β„“'
  _⊨_ Ο† ψ = ⟦ Ο† βŸ§α΅– ≀ ⟦ ψ βŸ§α΅–

  from-entails : Ο† ⊨ ψ β†’ ⟦ Ο† βŸ§α΅– ≀ ⟦ ψ βŸ§α΅–
  from-entails p = p

  instance
    H-Level-⊨ : βˆ€ {n} β†’ H-Level (Ο† ⊨ ψ) (suc n)
    H-Level-⊨ = prop-instance (has-is-thin _ _)

Having defined entailment, we can now build a deductive calculus on top of by constructing combinators corresponding to each proof rule. Most of the proofs are formalised straightforwardly, so we will restrict ourselves to pointing out which rule is being formalised.

The cut rule corresponds to transitivity of semantic entailment:

  cut : Ο† ⊨ ψ
      β†’ ψ ⊨ ΞΈ
      β†’ Ο† ⊨ ΞΈ
  cut p q = ≀-trans p q

The substitution rule follows from our previous correctness lemmas:

  sub-entail
    : (ρ : Sub Ξ“ Ξ”) {Ο† ψ : Formula Ξ”}
    β†’ Ο† ⊨ ψ
    β†’ sub-prop ρ Ο† ⊨ sub-prop ρ ψ
  sub-entail ρ {Ο†} {ψ} Ξ± =
    ⟦ sub-prop ρ Ο† βŸ§α΅–  P.=⟨ sub-prop-correct ρ Ο† ⟩P.=
    ⟦ Ο† βŸ§α΅– [ ⟦ ρ ⟧˒ ]  P.β‰€βŸ¨ subst-≀ ⟦ ρ ⟧˒ Ξ± ⟩P.≀
    ⟦ ψ βŸ§α΅– [ ⟦ ρ ⟧˒ ]  P.=˘⟨ sub-prop-correct ρ ψ ⟩P.=˘
    ⟦ sub-prop ρ ψ βŸ§α΅–  P.β‰€βˆŽ

The three rules for conjunction follow at once from the definition of fibrewise meets:

  `∧-intro
    : Ξ¦ ⊨ Ο†
    β†’ Ξ¦ ⊨ ψ
    β†’ Ξ¦ ⊨ Ο† `∧ ψ
  `∧-intro α β = &-univ α β

  `∧-elimβ‚—
    : Ξ¦ ⊨ Ο† `∧ ψ
    β†’ Ξ¦ ⊨ Ο†
  `∧-elimβ‚— Ξ± = ≀-trans Ξ± (fibrewise-meet.π₁ _ _)

  `∧-elimᡣ
    : Ξ¦ ⊨ Ο† `∧ ψ
    β†’ Ξ¦ ⊨ ψ
  `∧-elimα΅£ Ξ± = ≀-trans Ξ± (fibrewise-meet.Ο€β‚‚ _ _)

The rules for existentials are slightly fiddly, but they follow from the universal properties of co/cartesian lifts and Frobenius reciprocity:

  `βˆƒ-elim
    : Ξ¦         ⊨ `βˆƒ Ο†
    β†’ wk Ξ¦ `∧ Ο† ⊨ wk ψ
    β†’ Ξ¦         ⊨ ψ

  `βˆƒ-intro
    : βˆ€ {Ξ“} {Ξ¦ ψ} {t : Tm Ξ“ Ο„}
    β†’ Ξ¦ ⊨ sub-prop (t , stop) ψ
    β†’ Ξ¦ ⊨ `βˆƒ ψ
The proofs here have a bit more β€œunfortunately, proof assistants” than the others, so we’ll keep them in <details> too.
  `βˆƒ-elim {Ξ¦ = Ξ¦} {Ο† = Ο†} {ψ = ψ} Φ⊒exists Ο†βŠ’Οˆ =
    ⟦ Ξ¦ βŸ§α΅–                              P.β‰€βŸ¨ &-univ Φ⊒exists P.≀-refl ⟩P.≀
    ⟦ `βˆƒ Ο† βŸ§α΅– & ⟦ Ξ¦ βŸ§α΅–                  P.=⟨ frobenius π₁ ⟩P.=
    exists π₁ (⟦ Ο† βŸ§α΅– & ⟦ Ξ¦ βŸ§α΅– [ π₁ ])  P.=⟨ ap (exists π₁) &-comm ⟩P.=
    exists π₁ (⟦ Ξ¦ βŸ§α΅– [ π₁ ] & ⟦ Ο† βŸ§α΅–)  P.β‰€βŸ¨ Ο†βŠ’Οˆ' ⟩P.≀
    ⟦ ψ βŸ§α΅–                              P.β‰€βˆŽ
    where
    Ο†βŠ’Οˆ' : exists π₁ (⟦ Ξ¦ βŸ§α΅– [ π₁ ] & ⟦ Ο† βŸ§α΅–) ≀ ⟦ ψ βŸ§α΅–
    Ο†βŠ’Οˆ' = ≀-exists π₁
      (transport (apβ‚‚ _≀_
        (apβ‚‚ _&_ (sub-prop-correct (drop stop) Ξ¦ βˆ™ apβ‚‚ _[_] refl (idl _)) refl)
        (sub-prop-correct (drop stop) ψ βˆ™ apβ‚‚ _[_] refl (idl _)))
        Ο†βŠ’Οˆ)

  `βˆƒ-intro {Ξ“ = Ξ“} {Ξ¦ = Ξ¦} {ψ = ψ} {t = t} Ξ± = hom[ cancell Ο€β‚βˆ˜βŸ¨βŸ© ] $
         cocartesian.has-lift.lifting π₁ ⟦ ψ βŸ§α΅–
    β„™.∘' cartesian.has-lift.lifting ⟨ id , ⟦ t βŸ§α΅‰ ⟩ ⟦ ψ βŸ§α΅–
    β„™.∘' p
    where
    p : ⟦ Ξ¦ βŸ§α΅– ≀ (⟦ ψ βŸ§α΅– [ ⟨ id , ⟦ t βŸ§α΅‰ ⟩ ])
    p =
      ⟦ Ξ¦ βŸ§α΅–                               P.β‰€βŸ¨ Ξ± ⟩P.≀
      ⟦ sub-prop (t , stop) ψ βŸ§α΅–           P.=⟨ sub-prop-correct (t , stop) ψ ⟩P.=
      (⟦ ψ βŸ§α΅– [ ⟨ id , ⟦ t βŸ§α΅‰ ⟩ ])         P.β‰€βˆŽ

Local rewritingπŸ”—

Since we’re working with a very strict presyntax of formulas, there are many distinct syntactic-formulae with identical semantics. This strictness affords us good computational properties at the cost of mathematical properties: our syntax isn’t initial in any sense. Moreover, since the details of the entailment relation are kept opaque to encourage use of the combinators above, it’s not immediate to the outside world that it respects semantic equality.

We’ll remedy this in the following way: we can define an inductive relation which includes semantic equality but also includes combinators for β€œzooming in” part of a formula, ignoring the parts that don’t change, so that we can deploy semantic equality at precisely the place where it matters. Then, we provide combinators witnessing that the entailment respects

data _β‰ˆα΅—_ : Tm Ξ“ Ο„ β†’ Tm Ξ“ Ο„ β†’ TypeΟ‰ where
  sem : ⟦ t βŸ§α΅‰ ≑ ⟦ s βŸ§α΅‰ β†’ t β‰ˆα΅— s
  _,_ : t β‰ˆα΅— t₁ β†’ s β‰ˆα΅— s₁ β†’ (t , s) β‰ˆα΅— (t₁ , s₁)
  `π₁ : t β‰ˆα΅— t₁ β†’ `π₁ t β‰ˆα΅— `π₁ t₁
  `Ο€β‚‚ : t β‰ˆα΅— t₁ β†’ `Ο€β‚‚ t β‰ˆα΅— `Ο€β‚‚ t₁
  fun : βˆ€ {h : Hom ⟦ Ο„ βŸ§α΅— ⟦ Οƒ βŸ§α΅—} {t t₁ : Tm Ξ“ Ο„}
      β†’ t β‰ˆα΅— t₁
      β†’ fun {Ο„ = Ο„} {Οƒ} h t β‰ˆα΅— fun h t₁

data _β‰ˆα΅–_ : Formula Ξ“ β†’ Formula Ξ“ β†’ TypeΟ‰ where
  sem  : ⟦ Ο† βŸ§α΅– ≑ ⟦ ψ βŸ§α΅– β†’ Ο† β‰ˆα΅– ψ
  _`∧_ : Ο† β‰ˆα΅– Ο†' β†’ ψ β‰ˆα΅– ψ' β†’ (Ο† `∧ ψ) β‰ˆα΅– (Ο†' `∧ ψ')
  `βˆƒ   : Ο† β‰ˆα΅– ψ β†’ `βˆƒ Ο† β‰ˆα΅– `βˆƒ ψ
  pred : βˆ€ {p} β†’ t β‰ˆα΅— t₁ β†’ pred p t β‰ˆα΅– pred p t₁
  _=α΅–_ : βˆ€ {Ξ“ Ο„} {t t₁ s s₁ : Tm Ξ“ Ο„}
        β†’ t β‰ˆα΅— t₁ β†’ s β‰ˆα΅— s₁ β†’ (t =α΅– s) β‰ˆα΅– (t₁ =α΅– s₁)

The data types above are simply presentations of semantic equality: iff. However, once again contains redundancy which is mathematically irrelevant but handy when working in a proof assistant.

to-semα΅— : t β‰ˆα΅— t₁ β†’ ⟦ t βŸ§α΅‰ ≑ ⟦ t₁ βŸ§α΅‰
to-semα΅— (sem p) = p
to-semα΅— (p , q) = apβ‚‚ ⟨_,_⟩ (to-semα΅— p) (to-semα΅— q)
to-semα΅— (`π₁ p) = apβ‚‚ _∘_ refl (to-semα΅— p)
to-semα΅— (`Ο€β‚‚ p) = apβ‚‚ _∘_ refl (to-semα΅— p)
to-semα΅— (fun p) = apβ‚‚ _∘_ refl (to-semα΅— p)

to-semα΅– : Ο† β‰ˆα΅– ψ β†’ ⟦ Ο† βŸ§α΅– ≑ ⟦ ψ βŸ§α΅–
to-semα΅– (sem p)  = p
to-semα΅– (p `∧ q) = apβ‚‚ _&_ (to-semα΅– p) (to-semα΅– q)
to-semα΅– (`βˆƒ p)   = ap (exists π₁) (to-semα΅– p)
to-semα΅– (pred p) = apβ‚‚ _[_] refl (to-semα΅— p)
to-semα΅– (_=α΅–_ {Ξ“ = Ξ“} {Ο„ = Ο„} p q) = apβ‚‚ equ (to-semα΅— p) (to-semα΅— q) where
  equ : βˆ€ (h h' : Hom ⟦ Ξ“ ⟧ᢜ ⟦ Ο„ βŸ§α΅—) β†’ β„™.Ob[ ⟦ Ξ“ ⟧ᢜ ]
  equ h h' = exists ⟨ id , id ⟩ aye [ ⟨ h , h' ⟩ ]

We can then provide combinators for turning into or as appropriate.

opaque
  unfolding _⊨_

  β‰ˆβ†’entails : Ο† β‰ˆα΅– ψ β†’ entails Ο† ψ
  β‰ˆβ†’entails p = transport (apβ‚‚ _≀_ refl (to-semα΅– p)) P.≀-refl

  β‰ˆβ†’entails⁻ : Ο† β‰ˆα΅– ψ β†’ entails ψ Ο†
  β‰ˆβ†’entails⁻ p = transport (apβ‚‚ _≀_ refl (sym (to-semα΅– p))) P.≀-refl