namespace http://hku.hk/PL

/T The SEDEL type theory (simple type theory with distributive intersection types).
   Formalized with Bruno Oliveira's students during Florian Rabe's March 2019 visit at Hongkong University.
   
   See also https://i.cs.hku.hk/~bruno/papers/nested.pdf and https://i.cs.hku.hk/~bruno/papers/traits.pdf


// define some namespace prefixes❚

import lfx http://gl.mathhub.info/MMT/LFX/Subtyping
import lfrules scala://lf.mmt.kwarc.info/
import stdlit scala://uom.api.mmt.kwarc.info/

theory SEDEL : lfx:?LFSubtyped =
  # Syntactic Categories
  
  /T types and terms of simple type theory❙
  tp : type
  tm : tp ⟶ type# tm 1 prec -5

  # Judgments
  
  ## Equality of terms
  
  equal : {A} tm A ⟶ tm A ⟶ type  # 2  1 3 prec -5role Eq
  refl  : {A,X: tm A} X ≡ A X# refl 2 
  // admissible rule (admissibility proof by logical relation)❙
  cong  : {A,B,X,Y: tm A, F: tm A ⟶ tm B} X ≡ A Y ⟶ (F X) ≡ B (F Y)

  ## Subtyping
  sbt : tp ⟶ tp ⟶ type# 1 < 2 prec -5
  spt : tp ⟶ tp ⟶ type= [A,B] B < A# 1 > 2 prec -5
  
  /T proof irrelevance for subtyping proofs❙
  rule lfrules?TermIrrelevanceRule sbt

  st_refl: {A} A < A
  st_trans: {A,B,C} A < B ⟶ B < C ⟶ A < C

  /T embed SEDEL subtyping into LF subtyping❙
  st_shallow : {A,B} A < B ⟶ tm A <* tm B

  ## Relationship between Equality and Subtyping

  /T equality depends on the type at which terms are compared.
     Equality is preserved when going to larger types.
     But not necessarily the other way round, e.g., all terms are equal at unit type.❙ 
  // TODO: this should type-check❙
  // equal_st : {A,B,s:tm A, t: tm A} A < B ⟶ s ≡ A t ⟶ s ≡ B t❙  
  
  # Syntax

  ## Function Types
  
  fun : tp ⟶ tp ⟶ tp# 1  2
  fun_st : {A,B,C,D} A > C ⟶ B < D ⟶ A → B < C → D
  lambda : {A,B} (tm A ⟶ tm B) ⟶ tm (A → B)# λ 3
  apply : {A,B} tm (A → B) ⟶ tm A ⟶ tm B # 3 @ 4 prec 10
  
  beta : {A,B, F: tm A ⟶ tm B, a} (λ [x] F x) @ a ≡ B (F a) role Simplify# beta 3 4
  cong_apply : {A,B,F: tm (A → B), G, X, Y}
                 (F ≡ (A → B) G) ⟶ X ≡ A Y ⟶ (F @ X) ≡ B (G@Y)
  cong_lambda : {A,B,F: tm A ⟶ tm B,G} ({x} (F x) ≡ B (G x)) ⟶ (λ F) ≡ (A → B) (λ G)

  ## Top type

  /T The top type can be seen as the common supertype of all types, the unit type, and the neutral element of intersection.❙
  
  top : tp# 
  unit : tm ⊤
  st_top : {A} A < ⊤
  eq_top: {x: tm ⊤} x ≡ ⊤ unit

  ## Bottom type
  
  bottom : tp# 
  st_bottom: {A} ⊥ < A
  
  ## Intersection types
  
  inter : tp ⟶ tp ⟶ tp# 1 & 2 prec 10
  inter_st_left  : {A,B} A & B < A
  inter_st_right : {A,B} A & B < B
  inter_glb : {A,B,C} C < A ⟶ C < B ⟶ C < A & B
  
  disjoint: tp ⟶ tp ⟶ type# 1 * 2
  rule lfrules?TermIrrelevanceRule disjoint

  disjointIntro: {A,B} ({C} C > A ⟶ C > B ⟶ C > ⊤) ⟶ A * B
  disjointElim : {A,B} A * B ⟶ {C} C > A ⟶ C > B ⟶ C > ⊤
  
  // TODO: these definitions should type-check❙
  inter_left  : {A,B} tm A & B ⟶ tm A// = [A,B,x] x
  inter_right : {A,B} tm A & B ⟶ tm B// = [A,B,x] x
  inter_in    : {A,B} {a:tm A} {b: tm B} A * B ⟶ tm A & B# 3 ,, 4 %I5
  // TODO: this must solve the proof term (which is in the context) as an implicit argument❙
  // cong_inter_in : {A,B} A * B ⟶ {a1,a2: tm A, b1,b2: tm B}
                  a1 ≡ A a2 ⟶ b1 ≡ B b2 ⟶ (a1 ,,h b1) ≡ (A & B) (a2 ,, b2)❙ 
  
  /T Distributivity is natural but makes it harder to devise algorithms for subtyping.❙
  st_distrib: {A,B,C} (A → B) & (A → C) < A → (B & C)
  st_distrib_0 : {A} ⊤ < (A → ⊤)

  top_arrow : ⊤ < (⊤ → ⊤)= st_distrib_0 ⊤
  
  ## Literals

  nat  : tp
  zero : tm nat
  succ : tm nat ⟶ tm nat

  /T import lexing and computation rules for literals❙ 
  
  rule lfrules?Realize (tm nat) stdlit?StandardNat
  rule lfrules?Realize zero stdlit?Arithmetic/Zero
  rule lfrules?Realize succ stdlit?Arithmetic/Succ

  # Testing
  
  identity : {A} tm A → A  = [A] λ [x] x# id 1
  compose  : {A,B,C} tm A → B ⟶ tm B → C ⟶ tm A → C
           = [A,B,C,f,g] λ[x] g @ (f @ x)
    
  test: (id nat) @ (succ 1) ≡ nat 2= beta ([x] x) 2