Theory Type_Classes

section ‹Type Classes›

theory Type_Classes
  imports HOLCF_Main
begin


subsection ‹Eq class›

class Eq =
  fixes eq :: "'a  'a  tr"

text ‹
  The Haskell type class does allow /= to be specified separately. For now, we assume
  that all modeled type classes use the default implementation, or an equivalent.
›
fixrec neq :: "'a::Eq  'a  tr" where
  "neqxy = neg(eqxy)"

class Eq_strict = Eq +
  assumes eq_strict [simp]:
    "eqx = "
    "eqy = "

class Eq_sym = Eq_strict +
  assumes eq_sym: "eqxy = eqyx"

class Eq_equiv = Eq_sym +
  assumes eq_self_neq_FF [simp]: "eqxx  FF"
    and eq_trans: "eqxy = TT  eqyz = TT  eqxz = TT"
begin

lemma eq_refl: "eqxx    eqxx = TT"
  by (cases "eqxx") simp+

end

class Eq_eq = Eq_sym +
  assumes eq_self_neq_FF': "eqxx  FF"
    and eq_TT_dest: "eqxy = TT  x = y"
begin

subclass Eq_equiv
  by standard (auto simp: eq_self_neq_FF' dest: eq_TT_dest)

lemma eqD [dest]:
  "eqxy = TT  x = y"
  "eqxy = FF  x  y"
  by (auto elim: eq_TT_dest)

end

subsubsection ‹Class instances›

instantiation lift :: (countable) Eq_eq
begin

definition "eq  (Λ(Def x) (Def y). Def (x = y))"

instance
  by standard (auto simp: eq_lift_def flift1_def split: lift.splits)

end

lemma eq_ONE_ONE [simp]: "eqONEONE = TT"
  unfolding ONE_def eq_lift_def by simp


subsection ‹Ord class›

domain Ordering = LT | EQ | GT

definition oppOrdering :: "Ordering  Ordering" where
  "oppOrdering = (Λ x. case x of LT  GT | EQ  EQ | GT  LT)"

lemma oppOrdering_simps [simp]:
  "oppOrderingLT = GT"
  "oppOrderingEQ = EQ"
  "oppOrderingGT = LT"
  "oppOrdering = "
  unfolding oppOrdering_def by simp_all

class Ord = Eq +
  fixes compare :: "'a  'a  Ordering"
begin

definition lt :: "'a  'a  tr" where
  "lt = (Λ x y. case comparexy of LT  TT | EQ  FF | GT  FF)"

definition le :: "'a  'a  tr" where
  "le = (Λ x y. case comparexy of LT  TT | EQ  TT | GT  FF)"

lemma lt_eq_TT_iff: "ltxy = TT  comparexy = LT"
  by (cases "comparexy") (simp add: lt_def)+

end

class Ord_strict = Ord +
  assumes compare_strict [simp]:
    "comparey = "
    "comparex = "
begin

lemma lt_strict [simp]:
  shows "ltx = "
    and "ltx = "
  by (simp add: lt_def)+

lemma le_strict [simp]:
  shows "lex = "
    and "lex = "
  by (simp add: le_def)+

end

text ‹TODO: It might make sense to have a class for preorders too,
analogous to class eq_equiv›.›

class Ord_linear = Ord_strict +
  assumes eq_conv_compare: "eqxy = is_EQ(comparexy)"
    and oppOrdering_compare [simp]:
    "oppOrdering(comparexy) = compareyx"
    and compare_EQ_dest: "comparexy = EQ  x = y"
    and compare_self_below_EQ: "comparexx  EQ"
    and compare_LT_trans:
    "comparexy = LT  compareyz = LT  comparexz = LT"
  (*BH: Is this set of axioms complete?*)
  (*CS: How about totality of the order?*)
begin

lemma eq_TT_dest: "eqxy = TT  x = y"
  by (cases "comparexy") (auto dest: compare_EQ_dest simp: eq_conv_compare)+

lemma le_iff_lt_or_eq:
  "lexy = TT  ltxy = TT  eqxy = TT"
  by (cases "comparexy") (simp add: le_def lt_def eq_conv_compare)+

lemma compare_sym:
  "comparexy = (case compareyx of LT  GT | EQ  EQ | GT  LT)"
  by (subst oppOrdering_compare [symmetric]) (simp add: oppOrdering_def)

lemma compare_self_neq_LT [simp]: "comparexx  LT"
  using compare_self_below_EQ [of x] by clarsimp

lemma compare_self_neq_GT [simp]: "comparexx  GT"
  using compare_self_below_EQ [of x] by clarsimp

declare compare_self_below_EQ [simp]

lemma lt_trans: "ltxy = TT  ltyz = TT  ltxz = TT"
  unfolding lt_eq_TT_iff by (rule compare_LT_trans)

lemma compare_GT_iff_LT: "comparexy = GT  compareyx = LT"
  by (cases "comparexy", simp_all add: compare_sym [of y x])

lemma compare_GT_trans:
  "comparexy = GT  compareyz = GT  comparexz = GT"
  unfolding compare_GT_iff_LT by (rule compare_LT_trans)

lemma compare_EQ_iff_eq_TT:
  "comparexy = EQ  eqxy = TT"
  by (cases "comparexy") (simp add: is_EQ_def eq_conv_compare)+

lemma compare_EQ_trans:
  "comparexy = EQ  compareyz = EQ  comparexz = EQ"
  by (blast dest: compare_EQ_dest)

lemma le_trans:
  "lexy = TT  leyz = TT  lexz = TT"
  by (auto dest: eq_TT_dest lt_trans simp: le_iff_lt_or_eq)

lemma neg_lt: "neg(ltxy) = leyx"
  by (cases "comparexy", simp_all add: le_def lt_def compare_sym [of y x])

lemma neg_le: "neg(lexy) = ltyx"
  by (cases "comparexy", simp_all add: le_def lt_def compare_sym [of y x])

subclass Eq_eq
proof
  fix x y
  show "eqxy = eqyx"
    unfolding eq_conv_compare
    by (cases "comparexy", simp_all add: compare_sym [of y x])
  show "eqx = "
    unfolding eq_conv_compare by simp
  show "eqy = "
    unfolding eq_conv_compare by simp
  show "eqxx  FF"
    unfolding eq_conv_compare
    by (cases "comparexx", simp_all)
  show "x = y" if "eqxy = TT"
    using that
    unfolding eq_conv_compare
    by (cases "comparexy", auto dest: compare_EQ_dest)
qed

end

text ‹A combinator for defining Ord instances for datatypes.›

definition thenOrdering :: "Ordering  Ordering  Ordering" where
  "thenOrdering = (Λ x y. case x of LT  LT | EQ  y | GT  GT)"

lemma thenOrdering_simps [simp]:
  "thenOrderingLTy = LT"
  "thenOrderingEQy = y"
  "thenOrderingGTy = GT"
  "thenOrderingy = "
  unfolding thenOrdering_def by simp_all

lemma thenOrdering_LT_iff [simp]:
  "thenOrderingxy = LT  x = LT  x = EQ  y = LT"
  by (cases x, simp_all)

lemma thenOrdering_EQ_iff [simp]:
  "thenOrderingxy = EQ  x = EQ  y = EQ"
  by (cases x, simp_all)

lemma thenOrdering_GT_iff [simp]:
  "thenOrderingxy = GT  x = GT  x = EQ  y = GT"
  by (cases x, simp_all)

lemma thenOrdering_below_EQ_iff [simp]:
  "thenOrderingxy  EQ  x  EQ  (x =   y  EQ)"
  by (cases x) simp_all

lemma is_EQ_thenOrdering [simp]:
  "is_EQ(thenOrderingxy) = (is_EQx andalso is_EQy)"
  by (cases x) simp_all

lemma oppOrdering_thenOrdering:
  "oppOrdering(thenOrderingxy) =
    thenOrdering(oppOrderingx)(oppOrderingy)"
  by (cases x) simp_all

instantiation lift :: ("{linorder,countable}") Ord_linear
begin

definition
  "compare  (Λ (Def x) (Def y).
    if x < y then LT else if x > y then GT else EQ)"

instance proof
  fix x y z :: "'a lift"
  show "comparey = "
    unfolding compare_lift_def by simp
  show "comparex = "
    unfolding compare_lift_def by (cases x, simp_all)
  show "oppOrdering(comparexy) = compareyx"
    unfolding compare_lift_def
    by (cases x, cases y, simp, simp,
      cases y, simp, simp add: not_less less_imp_le)
  show "x = y" if "comparexy = EQ"
    using that
    unfolding compare_lift_def
    by (cases x, cases y, simp, simp,
        cases y, simp, simp split: if_splits)
  show "comparexz = LT" if "comparexy = LT" and "compareyz = LT"
    using that
    unfolding compare_lift_def
    by (cases x, simp, cases y, simp, cases z, simp,
        auto split: if_splits)
  show "eqxy = is_EQ(comparexy)"
    unfolding eq_lift_def compare_lift_def
    by (cases x, simp, cases y, simp, auto)
  show "comparexx  EQ"
    unfolding compare_lift_def
    by (cases x, simp_all)
qed

end

lemma lt_le:
  "lt(x::'a::Ord_linear)y = (lexy andalso neqxy)"
  by (cases "comparexy")
     (auto simp: lt_def le_def eq_conv_compare)

end