\import Category
\import Category.Functor
\import Category.Limit
\import Category.Product
\import Equiv
\import Function.Meta
\import Logic
\import Logic.Unique
\import Meta
\import Paths
\import Paths.Meta
\import Set.SetCategory
\open PrecatWithBprod

\class CartesianClosedPrecat \extends CartesianPrecat {
  | CHom : Ob -> Ob -> Ob
  | CHom-eval {X Y : Ob} : Hom (Bprod (CHom X Y) X) Y
  | CHom-univ {X Y Z : Ob} : IsEquiv {Hom Z (CHom X Y)} {Hom (Bprod Z X) Y} (\lam g => CHom-eval  prodMap g id)

  \func curry {X Y Z : Ob} (f : Hom (Bprod Z X) Y) : Hom Z (CHom X Y)
    => IsEquiv.ret CHom-univ f

  \func uncurry {X Y Z : Ob} (g : Hom Z (CHom X Y)) : Hom (Bprod Z X) Y
    => CHom-eval  prodMap g id

  \lemma curry_uncurry {X Y Z : Ob} {g : Hom Z (CHom X Y)} : curry (uncurry g) = g
    => IsEquiv.ret_f CHom-univ

  \lemma uncurry_curry {X Y Z : Ob} {f : Hom (Bprod Z X) Y} : uncurry (curry f) = f
    => IsEquiv.f_ret CHom-univ
}

\instance SetCartesianClosed.{u} : CartesianClosedPrecat (\Set u)
  | CartesianPrecat => SetBicat.{u}
  | CHom X Y => X -> Y
  | CHom-eval s => s.1 s.2
  | CHom-univ => inP \new QEquiv {
    | ret f z x => f (z,x)
    | ret_f => idpe
    | f_sec => idpe
  }