Theory CartesianClosedCategory
chapter "Cartesian Closed Category"
theory CartesianClosedCategory
imports CartesianCategory
begin
  text‹
    A \emph{cartesian closed category} is a cartesian category such that,
    for every object ‹b›, the functor ‹prod ‐ b› is a left adjoint functor.
    A right adjoint to this functor takes each object ‹c› to the \emph{exponential}
    ‹exp b c›.  The adjunction yields a natural bijection between ‹hom (prod a b) c›
    and ‹hom a (exp b c)›.
  ›
  locale cartesian_closed_category =
    cartesian_category +
  assumes left_adjoint_prod_ax: "⋀b. ide b ⟹ left_adjoint_functor C C (λx. some_prod x b)"
  locale elementary_cartesian_closed_category =
    elementary_cartesian_category C pr0 pr1 one trm
  for C :: "'a ⇒ 'a ⇒ 'a"  (infixr ‹⋅› 55)
  and pr0 :: "'a ⇒ 'a ⇒ 'a"  (‹𝔭⇩0[_, _]›)
  and pr1 :: "'a ⇒ 'a ⇒ 'a"  (‹𝔭⇩1[_, _]›)
  and one :: "'a"              (‹𝟭›)
  and trm :: "'a ⇒ 'a"        (‹𝗍[_]›)
  and exp :: "'a ⇒ 'a ⇒ 'a"
  and eval :: "'a ⇒ 'a ⇒ 'a"
  and curry :: "'a ⇒ 'a ⇒ 'a ⇒ 'a ⇒ 'a" +
  assumes eval_in_hom_ax: "⟦ ide b; ide c ⟧ ⟹ «eval b c : prod (exp b c) b → c»"
  and ide_exp_ax [intro]: "⟦ ide b; ide c ⟧ ⟹ ide (exp b c)"
  and curry_in_hom: "⟦ ide a; ide b; ide c; «g : prod a b → c» ⟧
                          ⟹ «curry a b c g : a → exp b c»"
  and uncurry_curry_ax: "⟦ ide a; ide b; ide c; «g : prod a b → c» ⟧
                          ⟹ eval b c ⋅ prod (curry a b c g) b = g"
  and curry_uncurry_ax: "⟦ ide a; ide b; ide c; «h : a → exp b c» ⟧
                          ⟹ curry a b c (eval b c ⋅ prod h b) = h"
  context cartesian_closed_category
  begin
    interpretation elementary_cartesian_category C some_pr0 some_pr1 ‹𝟭⇧?› ‹λa. 𝗍⇧?[a]›
      using extends_to_elementary_cartesian_category by blast
    lemma has_exponentials:
    assumes "ide b" and "ide c"
    shows "∃x e. ide x ∧ «e : x ⊗⇧? b → c» ∧
                 (∀a g. ide a ∧ «g : a ⊗⇧? b → c» ⟶ (∃!f. «f : a → x» ∧ g = e ⋅ (f ⊗⇧? b)))"
    proof -
      interpret F: left_adjoint_functor C C ‹λx. x ⊗⇧? b›
        using assms(1) left_adjoint_prod_ax by simp
      obtain x e where e: "terminal_arrow_from_functor C C (λx. x ⊗⇧? b) x c e"
        using assms F.ex_terminal_arrow [of c] by auto
      interpret e: terminal_arrow_from_functor C C ‹λx. x ⊗⇧? b› x c e
        using e by simp
      have "⋀a g. ⟦ ide a; «g : a ⊗⇧? b → c» ⟧ ⟹ ∃!f. «f : a → x» ∧ g = e ⋅ (f ⊗⇧? b)"
        using e.is_terminal category_axioms F.functor_axioms
        unfolding e.is_coext_def arrow_from_functor_def arrow_from_functor_axioms_def
        by simp
      thus ?thesis
        using e.arrow by metis
    qed
    definition some_exp  (‹exp⇧?›)
    where "some_exp b c ≡ SOME x. ide x ∧
                                    (∃e. «e : x ⊗⇧? b → c» ∧
                                      (∀a g. ide a ∧ «g : a ⊗⇧? b → c»
                                              ⟶ (∃!f. «f : a → x» ∧ g = e ⋅ (f ⊗⇧? b))))"
    definition some_eval  (‹eval⇧?›)
    where "some_eval b c ≡ SOME e. «e : exp⇧? b c ⊗⇧? b → c» ∧
                                     (∀a g. ide a ∧ «g : a ⊗⇧? b → c»
                                              ⟶ (∃!f. «f : a → exp⇧? b c» ∧ g = e ⋅(f ⊗⇧? b)))"
    definition some_Curry  (‹Curry⇧?›)
    where "some_Curry a b c g ≡ THE f. «f : a → exp⇧? b c» ∧ g = eval⇧? b c ⋅ (f ⊗⇧? b)"
    lemma Curry_uniqueness:
    assumes "ide b" and "ide c"
    shows "ide (exp⇧? b c)"
    and "«eval⇧? b c : exp⇧? b c ⊗⇧? b → c»"
    and "⟦ ide a; «g : a ⊗⇧? b → c» ⟧ ⟹
            ∃!f. «f : a → exp⇧? b c» ∧ g = eval⇧? b c ⋅ (f ⊗⇧? b)"
      using assms some_exp_def some_eval_def has_exponentials
            someI_ex [of "λx. ide x ∧ (∃e. «e : x ⊗⇧? b → c» ∧
                                           (∀a g. ide a ∧ «g : a ⊗⇧? b → c»
                                              ⟶ (∃!f. «f : a → x» ∧ g = e ⋅ (f ⊗⇧? b))))"]
            someI_ex [of "λe. «e : exp⇧? b c ⊗⇧? b → c» ∧
                              (∀a g. ide a ∧ «g : a ⊗⇧? b → c»
                                           ⟶ (∃!f. «f : a → exp⇧? b c» ∧ g = e ⋅ (f ⊗⇧? b)))"]
      by auto
    lemma ide_exp [intro, simp]:
    assumes "ide b" and "ide c"
    shows "ide (exp⇧? b c)"
      using assms Curry_uniqueness(1) by force
    lemma eval_in_hom [intro]:
    assumes "ide b" and "ide c" and "x = exp⇧? b c ⊗⇧? b"
    shows "«eval⇧? b c : x → c»"
      using assms Curry_uniqueness by simp
    lemma Uncurry_Curry:
    assumes "ide a" and "ide b" and "«g : a ⊗⇧? b → c»"
    shows "«Curry⇧? a b c g : a → exp⇧? b c» ∧ g = eval⇧? b c ⋅ (Curry⇧? a b c g ⊗⇧? b)"
    proof -
      have "ide c"
        using assms(3) by auto
      thus ?thesis
        using assms some_Curry_def Curry_uniqueness
              theI' [of "λf. «f : a → exp⇧? b c» ∧ g = eval⇧? b c ⋅ (f ⊗⇧? b)"]
        by simp
    qed
    lemma Curry_Uncurry:
    assumes "ide b" and "ide c" and "«h : a → exp⇧? b c»"
    shows "Curry⇧? a b c (eval⇧? b c ⋅ (h ⊗⇧? b)) = h"
    proof -
      have "∃!f. «f : a → exp⇧? b c» ∧ eval⇧? b c ⋅ (h ⊗⇧? b) = eval⇧? b c ⋅ (f ⊗⇧? b)"
      proof -
        have "ide a ∧ «eval⇧? b c ⋅ (h ⊗⇧? b) : (a ⊗⇧? b) → c»"
        proof (intro conjI)
          show "ide a"
            using assms(3) by auto
          show "«eval⇧? b c ⋅ (h ⊗⇧? b) : a ⊗⇧? b → c»"
            using assms by (intro comp_in_homI) auto
        qed
        thus ?thesis
          using assms Curry_uniqueness by simp
      qed
      moreover have "«h : a → exp⇧? b c» ∧ eval⇧? b c ⋅ (h ⊗⇧? b) = eval⇧? b c ⋅ (h ⊗⇧? b)"
        using assms by simp
      ultimately show ?thesis
        using assms some_Curry_def Curry_uniqueness Uncurry_Curry
              the1_equality [of "λf. «f : a → exp⇧? b c» ∧
                                     eval⇧? b c ⋅ (h ⊗⇧? b) = eval⇧? b c ⋅ (f ⊗⇧? b)"]
        by simp
    qed
    lemma Curry_in_hom [intro]:
    assumes "ide a" and "ide b" and "«g : a ⊗⇧? b → c»"
    shows "«Curry⇧? a b c g : a → exp⇧? b c»"
      using assms
      by (simp add: Uncurry_Curry)
    lemma Curry_simps [simp]:
    assumes "ide a" and "ide b" and "«g : a ⊗⇧? b → c»"
    shows "arr (Curry⇧? a b c g)"
    and "dom (Curry⇧? a b c g) = a"
    and "cod (Curry⇧? a b c g) = exp⇧? b c"
      using assms Curry_in_hom by blast+
    lemma eval_simps [simp]:
    assumes "ide b" and "ide c" and "x = (exp⇧? b c) ⊗⇧? b"
    shows "arr (eval⇧? b c)"
    and "dom (eval⇧? b c) = x"
    and "cod (eval⇧? b c) = c"
      using assms eval_in_hom by auto
    interpretation elementary_cartesian_closed_category C some_pr0 some_pr1
                     ‹𝟭⇧?› ‹λa. 𝗍⇧?[a]› some_exp some_eval some_Curry
      using Curry_uniqueness Uncurry_Curry Curry_Uncurry
      apply unfold_locales by auto
    lemma extends_to_elementary_cartesian_closed_category:
    shows "elementary_cartesian_closed_category C some_pr0 some_pr1
             𝟭⇧? (λa. 𝗍⇧?[a]) some_exp some_eval some_Curry"
      ..
    lemma has_as_exponential:
    assumes "ide b" and "ide c"
    shows "has_as_exponential b c (exp⇧? b c) (eval⇧? b c)"
    proof
      show "ide b" by fact
      show "ide (exp⇧? b c)"
        using assms by simp
      show "«some_eval b c : exp⇧? b c ⊗⇧? b → c»"
        using assms by auto
      show "⋀a g. ⟦ide a; «g : a ⊗⇧? b → c»⟧ ⟹
                     ∃!f. «f : a → exp⇧? b c» ∧ g = eval⇧? b c ⋅ (f ⊗⇧? b)"
        by (simp add: assms Curry_uniqueness(3))
    qed
    lemma has_as_exponential_iff:
    shows "has_as_exponential b c x e ⟷
           ide b ∧ «e : x ⊗⇧? b → c» ∧
           (∃h. «h : x → exp⇧? b c» ∧ e = eval⇧? b c ⋅ (h ⊗⇧? b) ∧ iso h)"
    proof
      assume 1: "has_as_exponential b c x e"
      moreover have 2: "has_as_exponential b c (exp⇧? b c) (eval⇧? b c)"
        using 1 ide_cod has_as_exponential_def in_homE
        by (metis has_as_exponential)
      ultimately show "ide b ∧ «e : x ⊗⇧? b → c» ∧
                       (∃h. «h : x → exp⇧? b c» ∧ e = eval⇧? b c ⋅ (h ⊗⇧? b) ∧ iso h)"
        by (metis exponentials_are_isomorphic(2) has_as_exponentialE)
      next
      assume 1: "ide b ∧ «e : x ⊗⇧? b → c» ∧
                 (∃h. «h : x → exp⇧? b c» ∧ e = eval⇧? b c ⋅ (h ⊗⇧? b) ∧ iso h)"
      have c: "ide c"
        using 1 ide_cod in_homE by metis
      have 2: "has_as_exponential b c (exp⇧? b c) (eval⇧? b c)"
        by (simp add: 1 c eval_in_hom_ax Curry_uniqueness(3) has_as_exponential_def)
      obtain h where h: "«h : x → exp⇧? b c» ∧ e = eval⇧? b c ⋅ (h ⊗⇧? b) ∧ iso h"
        using 1 by blast
      show "has_as_exponential b c x e"
      proof (unfold has_as_exponential_def, intro conjI)
        show "ide b" and "ide x" and "«e : x ⊗⇧? b → c»"
          using 1 h ide_dom by blast+
        show "∀y g. ide y ∧ «g : y ⊗⇧? b → c» ⟶ (∃!f. «f : y → x» ∧ g = e ⋅ (f ⊗⇧? b))"
        proof (intro allI impI)
          fix y g
          assume 3: "ide y ∧ «g : y ⊗⇧? b → c»"
          obtain k where k: "«k : y → exp⇧? b c» ∧ g = eval⇧? b c ⋅ (k ⊗⇧? b)"
            by (metis 3 ‹ide b› c Curry_uniqueness(3))
          show "∃!f. «f : y → x» ∧ g = e ⋅ (f ⊗⇧? b)"
          proof -
            let ?f = "inv h ⋅ k"
            have f: "«?f : y → x»"
              by (meson comp_in_homI inv_in_hom h k)
            moreover have "g = e ⋅ (?f ⊗⇧? b)"
            proof -
              have "e ⋅ some_prod ?f b = e ⋅ some_prod (inv h ⋅ k) (b ⋅ b)"
                by (simp add: 1)
              also have "... = e ⋅ (inv h ⊗⇧? b) ⋅ (k ⊗⇧? b)"
                by (metis ‹ide b› f arrI comp_ide_self interchange ide_compE)
              also have "... = (e ⋅ (inv h ⊗⇧? b)) ⋅ (k ⊗⇧? b)"
                using comp_assoc by simp
              also have "... = eval⇧? b c ⋅ (k ⊗⇧? b)"
                by (metis ‹«e : x ⊗⇧? b → c»› h ‹ide b› arrI inv_prod(1-2) ide_is_iso
                    inv_ide invert_side_of_triangle(2))
              also have "... = g"
                using k by blast
              finally show ?thesis by blast
            qed
            moreover have "⋀f'. «f' : y → x» ∧ g = e ⋅ (f' ⊗⇧? b) ⟹ f' = ?f"
            proof -
              fix f'
              assume f': "«f' : y → x» ∧ g = e ⋅ (f' ⊗⇧? b)"
              have "«h ⋅ f' : y → exp⇧? b c» ∧ g = eval⇧? b c ⋅ (h ⋅ f' ⊗⇧? b)"
                using f' h ‹ide b› comp_assoc interchange seqI' by fastforce
              hence "C h f' = C h ?f"
                by (metis ‹ide b› arrI c h k Curry_Uncurry invert_side_of_triangle(1))
              thus "f' = ?f"
                using f h iso_cancel_left by auto
            qed
            ultimately show ?thesis by blast
          qed
        qed
      qed
    qed
  end
  context elementary_cartesian_closed_category
  begin
    lemma left_adjoint_prod:
    assumes "ide b"
    shows "left_adjoint_functor C C (λx. x ⊗ b)"
    proof -
      interpret "functor" C C ‹λx. x ⊗ b›
        using assms interchange
        apply unfold_locales
            apply auto
        using prod_def tuple_def
        by auto
      interpret left_adjoint_functor C C ‹λx. x ⊗ b›
      proof
        show "⋀c. ide c ⟹ ∃x e. terminal_arrow_from_functor C C (λx. x ⊗ b) x c e"
        proof -
          fix c
          assume c: "ide c"
          show "∃x e. terminal_arrow_from_functor C C (λx. x ⊗ b) x c e"
          proof (intro exI)
            interpret arrow_from_functor C C ‹λx. x ⊗ b› ‹exp b c› c ‹eval b c›
              using assms c eval_in_hom_ax
              by (unfold_locales, auto)
            show "terminal_arrow_from_functor C C (λx. x ⊗ b) (exp b c) c (eval b c)"
            proof
              show "⋀a f. arrow_from_functor C C (λx. x ⊗ b) a c f ⟹
                            ∃!g. arrow_from_functor.is_coext C C
                                   (λx. x ⊗ b) (exp b c) (eval b c) a f g"
              proof -
                fix a f
                assume f: "arrow_from_functor C C (λx. x ⊗ b) a c f"
                interpret f: arrow_from_functor C C ‹λx. x ⊗ b› a c f
                  using f by simp
                show "∃!g. is_coext a f g"
                proof
                  have a: "ide a"
                    using f.arrow by simp
                  show "is_coext a f (curry a b c f)"
                    unfolding is_coext_def
                    using assms a c curry_in_hom uncurry_curry_ax f.arrow by simp
                  show "⋀g. is_coext a f g ⟹ g = curry a b c f"
                    unfolding is_coext_def
                    using assms a c curry_uncurry_ax f.arrow by simp
                qed
              qed
            qed
          qed
        qed
      qed
      show ?thesis ..
    qed
    sublocale cartesian_category C
      using is_cartesian_category by simp
    sublocale cartesian_closed_category C
    proof -
      interpret CCC: elementary_cartesian_category
                       C some_pr0 some_pr1 some_terminal some_terminator
        using extends_to_elementary_cartesian_category by blast
      show "cartesian_closed_category C"
      proof     
        fix b
        assume b: "ide b"
        interpret left_adjoint_functor C C ‹λx. CCC.prod x b›
        proof -
          
          have "naturally_isomorphic C C (λx. x ⊗ b) (λx. CCC.prod x b)"
          proof -
            interpret CC: product_category C C ..
            interpret X: binary_functor C C C ‹λfg. fst fg ⊗ snd fg›
              using binary_functor_Prod(1) by auto
            interpret Xb: "functor" C C ‹λx. x ⊗ b›
              using b X.fixing_ide_gives_functor_2 by simp
            interpret prod: binary_functor C C C ‹λfg. CCC.prod (fst fg) (snd fg)›
              using CCC.binary_functor_Prod(1) by simp
            interpret prod_b: "functor" C C ‹λx. CCC.prod x b›
               using b prod.fixing_ide_gives_functor_2 by simp
            interpret φ: transformation_by_components C C ‹λx. x ⊗ b› ‹λx. CCC.prod x b›
                           ‹λa. CCC.tuple 𝔭⇩1[a, b] 𝔭⇩0[a, b]›
              using b CCC.prod_tuple by unfold_locales auto
            interpret φ: natural_isomorphism C C ‹λx. x ⊗ b› ‹λx. CCC.prod x b› φ.map
            proof
              fix a
              assume a: "ide a"
              show "iso (φ.map a)"
              proof
                show "inverse_arrows (φ.map a) ⟨some_pr1 a b, some_pr0 a b⟩"
                  using a b by auto
              qed
            qed
            show ?thesis
              using naturally_isomorphic_def φ.natural_isomorphism_axioms by blast
          qed
          moreover have "left_adjoint_functor C C (λx. x ⊗ b)"
            using b left_adjoint_prod by simp
          ultimately show "left_adjoint_functor C C (λx. CCC.prod x b)"
            using left_adjoint_functor_respects_naturally_isomorphic by auto
        qed
        show "⋀f. ¬ arr f ⟹ some_prod f b = null"
          using extensionality by blast
        show "⋀g f. seq g f ⟹ some_prod (g ⋅ f) b = some_prod g b ⋅ some_prod f b"
          by simp
        show "⋀y. ide y ⟹ ∃x e. terminal_arrow_from_functor (⋅) (⋅) (λx. some_prod x b) x y e"
          using ex_terminal_arrow by simp
      qed auto
    qed
    lemma is_cartesian_closed_category:
    shows "cartesian_closed_category C"
      ..
  end
end