Theory TypeApp
section ‹Type Application›
theory TypeApp
imports HOLCF
begin
subsection ‹Class of type constructors›
text ‹In HOLCF, the type @{typ "udom defl"} consists of deflations
over the universal domain---each value of type @{typ "udom defl"}
represents a bifinite domain. In turn, values of the continuous
function type @{typ "udom defl → udom defl"} represent functions from
domains to domains, i.e.~type constructors.›
text ‹Class ‹tycon›, defined below, will be populated with
dummy types: For example, if the type ‹foo› is an instance of
class ‹tycon›, then users will never deal with any values ‹x::foo› in practice. Such types are only used with the overloaded
constant ‹tc›, which associates each type ‹'a::tycon›
with a value of type @{typ "udom defl → udom defl"}. \medskip›
class tycon =
fixes tc :: "('a::type) itself ⇒ udom defl → udom defl"
text ‹Type @{typ "'a itself"} is defined in Isabelle's meta-logic;
it is inhabited by a single value, written @{term "TYPE('a)"}. We
define the syntax ‹TC('a)› to abbreviate ‹tc
TYPE('a)›. \medskip›
syntax "_TC" :: "type ⇒ logic" ("(1TC/(1'(_')))")
translations "TC('a)" ⇌ "CONST tc TYPE('a)"
subsection ‹Type constructor for type application›
text ‹We now define a binary type constructor that models type
application: Type ‹('a, 't) app› is the result of applying the
type constructor ‹'t› (from class ‹tycon›) to the type
argument ‹'a› (from class ‹domain›).›
text ‹We define type ‹('a, 't) app› using ‹domaindef›,
a low-level type-definition command provided by HOLCF (similar to
‹typedef› in Isabelle/HOL) that defines a new domain type
represented by the given deflation. Note that in HOLCF, ‹DEFL('a)› is an abbreviation for ‹defl TYPE('a)›, where
‹defl :: ('a::domain) itself ⇒ udom defl› is an overloaded
function from the ‹domain› type class that yields the deflation
representing the given type. \medskip›
domaindef ('a,'t) app = "TC('t::tycon)⋅DEFL('a::domain)"
text ‹We define the infix syntax ‹'a⋅'t› for the type ‹('a,'t) app›. Note that for consistency with Isabelle's existing
type syntax, we have used postfix order for type application: type
argument on the left, type constructor on the right. \medskip›
type_notation app ("(_⋅_)" [999,1000] 999)
text ‹The ‹domaindef› command generates the theorem ‹DEFL_app›: @{thm DEFL_app [where 'a="'a::domain" and 't="'t::tycon"]},
which we can use to derive other useful lemmas. \medskip›
lemma TC_DEFL: "TC('t::tycon)⋅DEFL('a) = DEFL('a⋅'t)"
by (rule DEFL_app [symmetric])
lemma DEFL_app_mono [simp, intro]:
"DEFL('a) ⊑ DEFL('b) ⟹ DEFL('a⋅'t::tycon) ⊑ DEFL('b⋅'t)"
apply (simp add: DEFL_app)
apply (erule monofun_cfun_arg)
done
end
Theory Coerce
section ‹Coercion Operator›
theory Coerce
imports HOLCF
begin
subsection ‹Coerce›
text ‹The ‹domain› type class, which is the default type class
in HOLCF, fixes two overloaded functions: ‹emb::'a → udom› and
‹prj::udom → 'a›. By composing the ‹prj› and ‹emb›
functions together, we can coerce values between any two types in
class ‹domain›. \medskip›
definition coerce :: "'a → 'b"
where "coerce ≡ prj oo emb"
text ‹When working with proofs involving ‹emb›, ‹prj›,
and ‹coerce›, it is often difficult to tell at which types those
constants are being used. To alleviate this problem, we define special
input and output syntax to indicate the types. \medskip›
syntax
"_emb" :: "type ⇒ logic" ("(1EMB/(1'(_')))")
"_prj" :: "type ⇒ logic" ("(1PRJ/(1'(_')))")
"_coerce" :: "type ⇒ type ⇒ logic" ("(1COERCE/(1'(_,/ _')))")
translations
"EMB('a)" ⇀ "CONST emb :: 'a → udom"
"PRJ('a)" ⇀ "CONST prj :: udom → 'a"
"COERCE('a,'b)" ⇀ "CONST coerce :: 'a → 'b"
typed_print_translation ‹
let
fun emb_tr' (ctxt : Proof.context) (Type(_, [T, _])) [] =
Syntax.const @{syntax_const "_emb"} $ Syntax_Phases.term_of_typ ctxt T
fun prj_tr' ctxt (Type(_, [_, T])) [] =
Syntax.const @{syntax_const "_prj"} $ Syntax_Phases.term_of_typ ctxt T
fun coerce_tr' ctxt (Type(_, [T, U])) [] =
Syntax.const @{syntax_const "_coerce"} $
Syntax_Phases.term_of_typ ctxt T $ Syntax_Phases.term_of_typ ctxt U
in
[(@{const_syntax emb}, emb_tr'),
(@{const_syntax prj}, prj_tr'),
(@{const_syntax coerce}, coerce_tr')]
end
›
lemma beta_coerce: "coerce⋅x = prj⋅(emb⋅x)"
by (simp add: coerce_def)
lemma prj_emb: "prj⋅(emb⋅x) = coerce⋅x"
by (simp add: coerce_def)
lemma coerce_strict [simp]: "coerce⋅⊥ = ⊥"
by (simp add: coerce_def)
text ‹Certain type instances of ‹coerce› may reduce to the
identity function, ‹emb›, or ‹prj›. \medskip›
lemma coerce_eq_ID [simp]: "COERCE('a, 'a) = ID"
by (rule cfun_eqI, simp add: beta_coerce)
lemma coerce_eq_emb [simp]: "COERCE('a, udom) = EMB('a)"
by (rule cfun_eqI, simp add: beta_coerce)
lemma coerce_eq_prj [simp]: "COERCE(udom, 'a) = PRJ('a)"
by (rule cfun_eqI, simp add: beta_coerce)
text "Cancellation rules"
lemma emb_coerce:
"DEFL('a) ⊑ DEFL('b)
⟹ EMB('b)⋅(COERCE('a,'b)⋅x) = EMB('a)⋅x"
by (simp add: beta_coerce emb_prj_emb)
lemma coerce_prj:
"DEFL('a) ⊑ DEFL('b)
⟹ COERCE('b,'a)⋅(PRJ('b)⋅x) = PRJ('a)⋅x"
by (simp add: beta_coerce prj_emb_prj)
lemma coerce_idem [simp]:
"DEFL('a) ⊑ DEFL('b)
⟹ COERCE('b,'c)⋅(COERCE('a,'b)⋅x) = COERCE('a,'c)⋅x"
by (simp add: beta_coerce emb_prj_emb)
subsection ‹More lemmas about emb and prj›
lemma prj_cast_DEFL [simp]: "PRJ('a)⋅(cast⋅DEFL('a)⋅x) = PRJ('a)⋅x"
by (simp add: cast_DEFL)
lemma cast_DEFL_emb [simp]: "cast⋅DEFL('a)⋅(EMB('a)⋅x) = EMB('a)⋅x"
by (simp add: cast_DEFL)
text ‹@{term "DEFL(udom)"}›
lemma below_DEFL_udom [simp]: "A ⊑ DEFL(udom)"
apply (rule cast_below_imp_below)
apply (rule cast.belowI)
apply (simp add: cast_DEFL)
done
subsection ‹Coercing various datatypes›
text ‹Coercing from the strict product type @{typ "'a ⊗ 'b"} to
another strict product type @{typ "'c ⊗ 'd"} is equivalent to mapping
the ‹coerce› function separately over each component using
‹sprod_map :: ('a → 'c) → ('b → 'd) → 'a ⊗ 'b → 'c ⊗ 'd›. Each
of the several type constructors defined in HOLCF satisfies a similar
property, with respect to its own map combinator. \medskip›
lemma coerce_u: "coerce = u_map⋅coerce"
apply (rule cfun_eqI, simp add: coerce_def)
apply (simp add: emb_u_def prj_u_def liftemb_eq liftprj_eq)
apply (subst ep_pair.e_inverse [OF ep_pair_u])
apply (simp add: u_map_map cfcomp1)
done
lemma coerce_sfun: "coerce = sfun_map⋅coerce⋅coerce"
apply (rule cfun_eqI, simp add: coerce_def)
apply (simp add: emb_sfun_def prj_sfun_def)
apply (subst ep_pair.e_inverse [OF ep_pair_sfun])
apply (simp add: sfun_map_map cfcomp1)
done
lemma coerce_cfun': "coerce = cfun_map⋅coerce⋅coerce"
apply (rule cfun_eqI, simp add: prj_emb [symmetric])
apply (simp add: emb_cfun_def prj_cfun_def)
apply (simp add: prj_emb coerce_sfun coerce_u)
apply (simp add: encode_cfun_map [symmetric])
done
lemma coerce_ssum: "coerce = ssum_map⋅coerce⋅coerce"
apply (rule cfun_eqI, simp add: coerce_def)
apply (simp add: emb_ssum_def prj_ssum_def)
apply (subst ep_pair.e_inverse [OF ep_pair_ssum])
apply (simp add: ssum_map_map cfcomp1)
done
lemma coerce_sprod: "coerce = sprod_map⋅coerce⋅coerce"
apply (rule cfun_eqI, simp add: coerce_def)
apply (simp add: emb_sprod_def prj_sprod_def)
apply (subst ep_pair.e_inverse [OF ep_pair_sprod])
apply (simp add: sprod_map_map cfcomp1)
done
lemma coerce_prod: "coerce = prod_map⋅coerce⋅coerce"
apply (rule cfun_eqI, simp add: coerce_def)
apply (simp add: emb_prod_def prj_prod_def)
apply (subst ep_pair.e_inverse [OF ep_pair_prod])
apply (simp add: prod_map_map cfcomp1)
done
subsection ‹Simplifying coercions›
text ‹When simplifying applications of the ‹coerce› function,
rewrite rules are always oriented to replace ‹coerce› at complex
types with other applications of ‹coerce› at simpler types.›
text ‹The safest rewrite rules for ‹coerce› are given the
‹[simp]› attribute. For other rules that do not belong in the
global simpset, we use dynamic theorem list called ‹coerce_simp›,
which will collect additional rules for simplifying coercions. \medskip›
named_theorems coerce_simp "rule for simplifying coercions"
text ‹The ‹coerce› function commutes with data constructors
for various HOLCF datatypes. \medskip›
lemma coerce_up [simp]: "coerce⋅(up⋅x) = up⋅(coerce⋅x)"
by (simp add: coerce_u)
lemma coerce_sinl [simp]: "coerce⋅(sinl⋅x) = sinl⋅(coerce⋅x)"
by (simp add: coerce_ssum ssum_map_sinl')
lemma coerce_sinr [simp]: "coerce⋅(sinr⋅x) = sinr⋅(coerce⋅x)"
by (simp add: coerce_ssum ssum_map_sinr')
lemma coerce_spair [simp]: "coerce⋅(:x, y:) = (:coerce⋅x, coerce⋅y:)"
by (simp add: coerce_sprod sprod_map_spair')
lemma coerce_Pair [simp]: "coerce⋅(x, y) = (coerce⋅x, coerce⋅y)"
by (simp add: coerce_prod)
lemma beta_coerce_cfun [simp]: "coerce⋅f⋅x = coerce⋅(f⋅(coerce⋅x))"
by (simp add: coerce_cfun')
lemma coerce_cfun: "coerce⋅f = coerce oo f oo coerce"
by (simp add: cfun_eqI)
lemma coerce_cfun_app [coerce_simp]:
"coerce⋅f = (Λ x. coerce⋅(f⋅(coerce⋅x)))"
by (simp add: cfun_eqI)
end
Theory Functor
section ‹Functor Class›
theory Functor
imports TypeApp Coerce
keywords "tycondef" :: thy_defn and "⋅"
begin
subsection ‹Class definition›
text ‹Here we define the ‹functor› class, which models the
Haskell class \texttt{Functor}. For technical reasons, we split the
definition of ‹functor› into two separate classes: First, we
introduce ‹prefunctor›, which only requires ‹fmap› to
preserve the identity function, and not function composition.›
text ‹The Haskell class \texttt{Functor f} fixes a polymorphic
function \texttt{fmap :: (a -> b) -> f a -> f b}. Since functions in
Isabelle type classes can only mention one type variable, we have the
‹prefunctor› class fix a function ‹fmapU› that fixes both
of the polymorphic types to be the universal domain. We will use the
coercion operator to recover a polymorphic ‹fmap›.›
text ‹The single axiom of the ‹prefunctor› class is stated in
terms of the HOLCF constant ‹isodefl›, which relates a function
‹f :: 'a → 'a› with a deflation ‹t :: udom defl›:
@{thm isodefl_def [of f t, no_vars]}.›
class prefunctor = "tycon" +
fixes fmapU :: "(udom → udom) → udom⋅'a → udom⋅'a::tycon"
assumes isodefl_fmapU:
"isodefl (fmapU⋅(cast⋅t)) (TC('a::tycon)⋅t)"
text ‹The ‹functor› class extends ‹prefunctor› with an
axiom stating that ‹fmapU› preserves composition.›
class "functor" = prefunctor +
assumes fmapU_fmapU [coerce_simp]:
"⋀f g (xs::udom⋅'a::tycon).
fmapU⋅f⋅(fmapU⋅g⋅xs) = fmapU⋅(Λ x. f⋅(g⋅x))⋅xs"
text ‹We define the polymorphic ‹fmap› by coercion from ‹fmapU›, then we proceed to derive the polymorphic versions of the
functor laws.›
definition fmap :: "('a → 'b) → 'a⋅'f → 'b⋅'f::functor"
where "fmap = coerce⋅(fmapU :: _ → udom⋅'f → udom⋅'f)"
subsection ‹Polymorphic functor laws›
lemma fmapU_eq_fmap: "fmapU = fmap"
by (simp add: fmap_def eta_cfun)
lemma fmap_eq_fmapU: "fmap = fmapU"
by (simp only: fmapU_eq_fmap)
lemma cast_TC:
"cast⋅(TC('f)⋅t) = emb oo fmapU⋅(cast⋅t) oo PRJ(udom⋅'f::prefunctor)"
by (rule isodefl_fmapU [unfolded isodefl_def])
lemma isodefl_cast: "isodefl (cast⋅t) t"
by (simp add: isodefl_def)
lemma cast_cast_below1: "A ⊑ B ⟹ cast⋅A⋅(cast⋅B⋅x) = cast⋅A⋅x"
by (intro deflation_below_comp1 deflation_cast monofun_cfun_arg)
lemma cast_cast_below2: "A ⊑ B ⟹ cast⋅B⋅(cast⋅A⋅x) = cast⋅A⋅x"
by (intro deflation_below_comp2 deflation_cast monofun_cfun_arg)
lemma isodefl_fmap:
assumes "isodefl d t"
shows "isodefl (fmap⋅d :: 'a⋅'f → _) (TC('f::functor)⋅t)"
proof -
have deflation_d: "deflation d"
using assms by (rule isodefl_imp_deflation)
have cast_t: "cast⋅t = emb oo d oo prj"
using assms unfolding isodefl_def .
have t_below: "t ⊑ DEFL('a)"
apply (rule cast_below_imp_below)
apply (simp only: cast_t cast_DEFL)
apply (simp add: cfun_below_iff deflation.below [OF deflation_d])
done
have fmap_eq: "fmap⋅d = PRJ('a⋅'f) oo cast⋅(TC('f)⋅t) oo emb"
by (simp add: fmap_def coerce_cfun cast_TC cast_t prj_emb cfcomp1)
show ?thesis
apply (simp add: fmap_eq isodefl_def cfun_eq_iff emb_prj)
apply (simp add: DEFL_app)
apply (simp add: cast_cast_below1 monofun_cfun t_below)
apply (simp add: cast_cast_below2 monofun_cfun t_below)
done
qed
lemma fmap_ID [simp]: "fmap⋅ID = ID"
apply (rule isodefl_DEFL_imp_ID)
apply (subst DEFL_app)
apply (rule isodefl_fmap)
apply (rule isodefl_ID_DEFL)
done
lemma fmap_ident [simp]: "fmap⋅(Λ x. x) = ID"
by (simp add: ID_def [symmetric])
lemma coerce_coerce_eq_fmapU_cast [coerce_simp]:
fixes xs :: "udom⋅'f::functor"
shows "COERCE('a⋅'f, udom⋅'f)⋅(COERCE(udom⋅'f, 'a⋅'f)⋅xs) =
fmapU⋅(cast⋅DEFL('a))⋅xs"
by (simp add: coerce_def emb_prj DEFL_app cast_TC)
lemma fmap_fmap:
fixes xs :: "'a⋅'f::functor" and g :: "'a → 'b" and f :: "'b → 'c"
shows "fmap⋅f⋅(fmap⋅g⋅xs) = fmap⋅(Λ x. f⋅(g⋅x))⋅xs"
unfolding fmap_def
by (simp add: coerce_simp)
lemma fmap_cfcomp: "fmap⋅(f oo g) = fmap⋅f oo fmap⋅g"
by (simp add: cfcomp1 fmap_fmap eta_cfun)
subsection ‹Derived properties of ‹fmap››
text ‹Other theorems about ‹fmap› can be derived using only
the abstract functor laws.›
lemma deflation_fmap:
"deflation d ⟹ deflation (fmap⋅d)"
apply (rule deflation.intro)
apply (simp add: fmap_fmap deflation.idem eta_cfun)
apply (subgoal_tac "fmap⋅d⋅x ⊑ fmap⋅ID⋅x", simp)
apply (rule monofun_cfun_fun, rule monofun_cfun_arg)
apply (erule deflation.below_ID)
done
lemma ep_pair_fmap:
"ep_pair e p ⟹ ep_pair (fmap⋅e) (fmap⋅p)"
apply (rule ep_pair.intro)
apply (simp add: fmap_fmap ep_pair.e_inverse)
apply (simp add: fmap_fmap)
apply (rule_tac y="fmap⋅ID⋅y" in below_trans)
apply (rule monofun_cfun_fun)
apply (rule monofun_cfun_arg)
apply (rule cfun_belowI, simp)
apply (erule ep_pair.e_p_below)
apply simp
done
lemma fmap_strict:
fixes f :: "'a → 'b"
assumes "f⋅⊥ = ⊥" shows "fmap⋅f⋅⊥ = (⊥::'b⋅'f::functor)"
proof (rule bottomI)
have "fmap⋅f⋅(⊥::'a⋅'f) ⊑ fmap⋅f⋅(fmap⋅⊥⋅(⊥::'b⋅'f))"
by (simp add: monofun_cfun)
also have "... = fmap⋅(Λ x. f⋅(⊥⋅x))⋅(⊥::'b⋅'f)"
by (simp add: fmap_fmap)
also have "... ⊑ fmap⋅ID⋅⊥"
by (simp add: monofun_cfun assms del: fmap_ID)
also have "... = ⊥"
by simp
finally show "fmap⋅f⋅⊥ ⊑ (⊥::'b⋅'f::functor)" .
qed
subsection ‹Proving that ‹fmap⋅coerce = coerce››
lemma fmapU_cast_eq:
"fmapU⋅(cast⋅A) =
PRJ(udom⋅'f) oo cast⋅(TC('f::functor)⋅A) oo emb"
by (subst cast_TC, rule cfun_eqI, simp)
lemma fmapU_cast_DEFL:
"fmapU⋅(cast⋅DEFL('a)) =
PRJ(udom⋅'f) oo cast⋅DEFL('a⋅'f::functor) oo emb"
by (simp add: fmapU_cast_eq DEFL_app)
lemma coerce_functor: "COERCE('a⋅'f, 'b⋅'f::functor) = fmap⋅coerce"
apply (rule cfun_eqI, rename_tac xs)
apply (simp add: fmap_def coerce_cfun)
apply (simp add: coerce_def)
apply (simp add: cfcomp1)
apply (simp only: emb_prj)
apply (subst fmapU_fmapU [symmetric])
apply (simp add: fmapU_cast_DEFL)
apply (simp add: emb_prj)
apply (simp add: cast_cast_below1 cast_cast_below2)
done
subsection ‹Lemmas for reasoning about coercion›
lemma fmapU_cast_coerce [coerce_simp]:
fixes m :: "'a⋅'f::functor"
shows "fmapU⋅(cast⋅DEFL('a))⋅(COERCE('a⋅'f, udom⋅'f)⋅m) =
COERCE('a⋅'f, udom⋅'f)⋅m"
by (simp add: coerce_functor cast_DEFL fmapU_eq_fmap fmap_fmap eta_cfun)
lemma coerce_fmap [coerce_simp]:
fixes xs :: "'a⋅'f::functor" and f :: "'a → 'b"
shows "COERCE('b⋅'f, 'c⋅'f)⋅(fmap⋅f⋅xs) = fmap⋅(Λ x. COERCE('b,'c)⋅(f⋅x))⋅xs"
by (simp add: coerce_functor fmap_fmap)
lemma fmap_coerce [coerce_simp]:
fixes xs :: "'a⋅'f::functor" and f :: "'b → 'c"
shows "fmap⋅f⋅(COERCE('a⋅'f, 'b⋅'f)⋅xs) = fmap⋅(Λ x. f⋅(COERCE('a,'b)⋅x))⋅xs"
by (simp add: coerce_functor fmap_fmap)
subsection ‹Configuration of Domain package›
text ‹We make various theorem declarations to enable Domain
package definitions that involve ‹tycon› application.›
setup ‹Domain_Take_Proofs.add_rec_type (@{type_name app}, [true, false])›
declare DEFL_app [domain_defl_simps]
declare fmap_ID [domain_map_ID]
declare deflation_fmap [domain_deflation]
declare isodefl_fmap [domain_isodefl]
subsection ‹Configuration of the Tycon package›
text ‹We now set up a new type definition command, which is used for
defining new ‹tycon› instances. The ‹tycondef› command
is implemented using much of the same code as the Domain package,
and supports a similar input syntax. It automatically generates a
‹prefunctor› instance for each new type. (The user must
provide a proof of the composition law to obtain a ‹functor›
class instance.)›
ML_file ‹tycondef.ML›
end
File ‹tycondef.ML›
signature TYCON =
sig
val add_tycon_cmd:
(string * (string * string option) list * binding * mixfix *
(binding * (bool * binding option * string) list * mixfix) list) list
-> theory -> theory
val add_tycon:
(string * (string * sort) list * binding * mixfix *
(binding * (bool * binding option * typ) list * mixfix) list) list
-> theory -> theory
end
structure Tycon : TYCON =
struct
val TC_simp =
@{lemma "f ≡ g ⟹ f TYPE('a::{}) = g TYPE('a)" by simp}
fun mk_appT T U = Type (@{type_name app}, [T, U])
fun dest_appT (Type (@{type_name app}, [T, U])) = (T, U)
| dest_appT T = raise TYPE ("dest_appT", [T], [])
open HOLCF_Library
fun first (x,_,_) = x
fun second (_,x,_) = x
val beta_ss =
simpset_of (put_simpset HOL_basic_ss @{context}
addsimps @{thms simp_thms} addsimprocs [@{simproc beta_cfun_proc}])
fun is_cpo thy T = Sign.of_sort thy (T, @{sort cpo})
fun define_singleton_type
(typ : binding * (string * sort) list * mixfix)
(thy : theory) : (string * Typedef.info) * theory =
let
val set = @{term "UNIV :: unit set"}
val opt_morphs = NONE
fun tac ctxt = resolve_tac ctxt [UNIV_witness] 1
val _ = writeln ("Defining type " ^ Binding.print (first typ))
in
thy
|> Named_Target.theory_map_result (apsnd o Typedef.transform_info)
(Typedef.add_typedef {overloaded = false} typ set opt_morphs tac)
end
infixr 6 ->>
infixr -->>
val udomT = @{typ udom}
val deflT = @{typ "udom defl"}
val udeflT = @{typ "udom u defl"}
fun mk_DEFL T =
Const (@{const_name defl}, Term.itselfT T --> deflT) $ Logic.mk_type T
fun dest_DEFL (Const (@{const_name defl}, _) $ t) = Logic.dest_type t
| dest_DEFL t = raise TERM ("dest_DEFL", [t])
fun mk_LIFTDEFL T =
Const (@{const_name liftdefl}, Term.itselfT T --> udeflT) $ Logic.mk_type T
fun dest_LIFTDEFL (Const (@{const_name liftdefl}, _) $ t) = Logic.dest_type t
| dest_LIFTDEFL t = raise TERM ("dest_LIFTDEFL", [t])
fun tc_const T =
Const (@{const_name tc}, Term.itselfT T --> deflT ->> deflT)
fun mk_TC T = tc_const T $ Logic.mk_type T
fun argumentTs (Type (@{type_name app}, [T, Type (_, Ts)])) = Ts @ [T]
| argumentTs (Type (_, Ts)) = Ts
| argumentTs T = []
fun mk_u_defl t = mk_capply (@{const "u_defl"}, t)
fun emb_const T = Const (@{const_name emb}, T ->> udomT)
fun prj_const T = Const (@{const_name prj}, udomT ->> T)
fun coerce_const (T, U) = mk_cfcomp (prj_const U, emb_const T)
fun isodefl_const T =
Const (@{const_name isodefl}, (T ->> T) --> deflT --> HOLogic.boolT)
fun isodefl'_const T =
Const (@{const_name isodefl'}, (T ->> T) --> udeflT --> HOLogic.boolT)
fun mk_deflation t =
Const (@{const_name deflation}, Term.fastype_of t --> boolT) $ t
fun dest_eqs t = HOLogic.dest_eq (HOLogic.dest_Trueprop t)
fun mk_eqs (t, u) = HOLogic.mk_Trueprop (HOLogic.mk_eq (t, u))
fun deflation_abs_rep (info : Domain_Take_Proofs.iso_info) : thm =
let
val abs_iso = #abs_inverse info
val rep_iso = #rep_inverse info
val thm = @{thm deflation_abs_rep} OF [abs_iso, rep_iso]
in
Drule.zero_var_indexes thm
end
fun mk_projs [] _ = []
| mk_projs (x::[]) t = [(x, t)]
| mk_projs (x::xs) t = (x, mk_fst t) :: mk_projs xs (mk_snd t)
fun add_fixdefs
(spec : (binding * term) list)
(thy : theory) : (thm list * thm list * thm) * theory =
let
val binds = map fst spec
val (lhss, rhss) = ListPair.unzip (map (dest_eqs o snd) spec)
val functional = lambda_tuple lhss (mk_tuple rhss)
val fixpoint = mk_fix (mk_cabs functional)
val projs = mk_projs lhss fixpoint
fun mk_eqn (lhs, rhs) =
case lhs of
Const (@{const_name Rep_cfun}, _) $ f $ (x as Free _) =>
mk_eqn (f, big_lambda x rhs)
| f $ Const (@{const_name Pure.type}, T) =>
mk_eqn (f, Abs ("t", T, rhs))
| Const _ => Logic.mk_equals (lhs, rhs)
| _ => raise TERM ("lhs not of correct form", [lhs, rhs])
val eqns = map mk_eqn projs
val (fixdef_thms, thy) =
(Global_Theory.add_defs false o map Thm.no_attributes)
(map (Binding.suffix_name "_def") binds ~~ eqns) thy
fun prove_proj (lhs, rhs) =
let
fun tac ctxt = rewrite_goals_tac ctxt fixdef_thms THEN
simp_tac (put_simpset beta_ss ctxt) 1
val goal = Logic.mk_equals (lhs, rhs)
in Goal.prove_global thy [] [] goal (tac o #context) end
val proj_thms = map prove_proj projs
fun pair_equalI (thm1, thm2) = @{thm Pair_equalI} OF [thm1, thm2]
val tuple_fixdef_thm = foldr1 pair_equalI proj_thms
val cont_thm =
let
val ctxt = Proof_Context.init_global thy
val prop = mk_trp (mk_cont functional)
val rules = Named_Theorems.get ctxt @{named_theorems cont2cont}
val tac = REPEAT_ALL_NEW (match_tac ctxt (rev rules)) 1
in
Goal.prove_global thy [] [] prop (K tac)
end
val tuple_unfold_thm =
(@{thm def_cont_fix_eq} OF [tuple_fixdef_thm, cont_thm])
|> Local_Defs.unfold (Proof_Context.init_global thy) @{thms split_conv}
fun mk_unfold_thms [] _ = []
| mk_unfold_thms (n::[]) thm = [(n, thm)]
| mk_unfold_thms (n::ns) thm = let
val thmL = thm RS @{thm Pair_eqD1}
val thmR = thm RS @{thm Pair_eqD2}
in (n, thmL) :: mk_unfold_thms ns thmR end
val unfold_binds = map (Binding.suffix_name "_unfold") binds
val (unfold_thms, thy) =
(Global_Theory.add_thms o map (Thm.no_attributes o apsnd Drule.zero_var_indexes))
(mk_unfold_thms unfold_binds tuple_unfold_thm) thy
in
((proj_thms, unfold_thms, cont_thm), thy)
end
fun defl_of_typ
(thy : theory)
(rules' : (term * term) list)
(T : typ) : term =
let
val defl_simps = Global_Theory.get_thms thy "domain_defl_simps"
val rules = map (Thm.concl_of #> HOLogic.dest_Trueprop #> HOLogic.dest_eq) defl_simps
fun proc1 t =
(case dest_DEFL t of
TFree (a, _) => SOME (Free ("d" ^ Library.unprefix "'" a, deflT))
| _ => NONE) handle TERM _ => NONE
fun proc2 t =
(case dest_LIFTDEFL t of
TFree (a, _) => SOME (Free ("p" ^ Library.unprefix "'" a, udeflT))
| _ => NONE) handle TERM _ => NONE
in
Pattern.rewrite_term thy (rules @ rules') [proc1, proc2] (mk_DEFL T)
end
fun define_const
(bind : binding, rhs : term)
(thy : theory)
: (term * thm) * theory =
let
val typ = Term.fastype_of rhs
val (const, thy) = Sign.declare_const_global ((bind, typ), NoSyn) thy
val eqn = Logic.mk_equals (const, rhs)
val def = Thm.no_attributes (Binding.suffix_name "_def" bind, eqn)
val (def_thm, thy) = yield_singleton (Global_Theory.add_defs false) def thy
in
((const, def_thm), thy)
end
fun add_qualified_thm name (dbind, thm) =
yield_singleton Global_Theory.add_thms
((Binding.qualify_name true dbind name, thm), [])
fun define_map_functions
(spec : (binding * Domain_Take_Proofs.iso_info) list)
(thy : theory) =
let
val dbinds = map fst spec
val iso_infos = map snd spec
val dom_eqns = map (fn x => (#absT x, #repT x)) iso_infos
val rep_abs_consts = map (fn x => (#rep_const x, #abs_const x)) iso_infos
fun mapT T =
map (fn T => T ->> T) (filter (is_cpo thy) (argumentTs T)) -->> (T ->> T)
fun declare_map_const (tbind, (lhsT, _)) thy =
let
val map_type = mapT lhsT
val map_bind = Binding.suffix_name "_map" tbind
in
Sign.declare_const_global ((map_bind, map_type), NoSyn) thy
end
val (map_consts, thy) = thy |>
fold_map declare_map_const (dbinds ~~ dom_eqns)
local
fun unprime a = Library.unprefix "'" a
fun mapvar T = Free (unprime (fst (dest_TFree T)), T ->> T)
fun map_lhs (map_const, lhsT) =
(lhsT, list_ccomb (map_const, map mapvar (filter (is_cpo thy) (argumentTs lhsT))))
val tab1 = map map_lhs (map_consts ~~ map fst dom_eqns)
val Ts = (argumentTs o fst o hd) dom_eqns
val tab = (Ts ~~ map mapvar Ts) @ tab1
fun mk_map_spec (((rep_const, abs_const), _), (lhsT, rhsT)) =
let
val lhs = Domain_Take_Proofs.map_of_typ thy tab lhsT
val body = Domain_Take_Proofs.map_of_typ thy tab rhsT
val rhs = mk_cfcomp (abs_const, mk_cfcomp (body, rep_const))
in mk_eqs (lhs, rhs) end
in
val map_specs =
map mk_map_spec (rep_abs_consts ~~ map_consts ~~ dom_eqns)
end
val map_binds = map (Binding.suffix_name "_map") dbinds
val ((map_apply_thms, map_unfold_thms, map_cont_thm), thy) =
add_fixdefs (map_binds ~~ map_specs) thy
val deflation_abs_rep_thms = map deflation_abs_rep iso_infos
val deflation_map_thm =
let
fun unprime a = Library.unprefix "'" a
fun mk_f T = Free (unprime (fst (dest_TFree T)), T ->> T)
fun mk_assm T = mk_trp (mk_deflation (mk_f T))
fun mk_goal (map_const, (lhsT, _)) =
let
val Ts = argumentTs lhsT
val map_term = list_ccomb (map_const, map mk_f (filter (is_cpo thy) Ts))
in mk_deflation map_term end
val assms = (map mk_assm o filter (is_cpo thy) o argumentTs o fst o hd) dom_eqns
val goals = map mk_goal (map_consts ~~ dom_eqns)
val goal = mk_trp (foldr1 HOLogic.mk_conj goals)
val adm_rules =
@{thms adm_conj adm_subst [OF _ adm_deflation]
cont2cont_fst cont2cont_snd cont_id}
val bottom_rules =
@{thms fst_strict snd_strict deflation_bottom simp_thms}
val tuple_rules =
@{thms split_def fst_conv snd_conv}
val deflation_rules =
@{thms conjI deflation_ID}
@ deflation_abs_rep_thms
@ Domain_Take_Proofs.get_deflation_thms thy
in
Goal.prove_global thy [] assms goal (fn {prems, context = ctxt, ...} =>
EVERY
[rewrite_goals_tac ctxt map_apply_thms,
resolve_tac ctxt [map_cont_thm RS @{thm cont_fix_ind}] 1,
REPEAT (resolve_tac ctxt adm_rules 1),
simp_tac (put_simpset HOL_basic_ss ctxt addsimps bottom_rules) 1,
simp_tac (put_simpset HOL_basic_ss ctxt addsimps tuple_rules) 1,
REPEAT (eresolve_tac ctxt @{thms conjE} 1),
REPEAT (resolve_tac ctxt (deflation_rules @ prems) 1 ORELSE assume_tac ctxt 1)])
end
fun conjuncts [] _ = []
| conjuncts (n::[]) thm = [(n, thm)]
| conjuncts (n::ns) thm = let
val thmL = thm RS @{thm conjunct1}
val thmR = thm RS @{thm conjunct2}
in (n, thmL):: conjuncts ns thmR end
val deflation_map_binds = dbinds |>
map (Binding.prefix_name "deflation_" o Binding.suffix_name "_map")
val (deflation_map_thms, thy) = thy |>
(Global_Theory.add_thms o map (Thm.no_attributes o apsnd Drule.zero_var_indexes))
(conjuncts deflation_map_binds deflation_map_thm)
val thy = fold Domain_Take_Proofs.add_deflation_thm deflation_map_thms thy
val result =
{
map_consts = map_consts,
map_apply_thms = map_apply_thms,
map_unfold_thms = map_unfold_thms,
map_cont_thm = map_cont_thm,
deflation_map_thms = deflation_map_thms
}
in
(result, thy)
end
fun domain_isomorphism
(param : string)
(doms : ((string * sort) list * binding * mixfix * typ *
(binding * binding) option) list)
(thy: theory)
: (Domain_Take_Proofs.iso_info list
* Domain_Take_Proofs.take_induct_info) * theory =
let
val tmp_thy = thy |>
Sign.add_types_global (map (fn (tvs, tbind, mx, _, _) =>
(tbind, length tvs, mx)) doms)
val tmp_thy =
let
fun arity (vs, tbind, _, _, _) =
(Sign.full_name thy tbind, map snd vs, @{sort "tycon"})
in
fold Axclass.arity_axiomatization (map arity doms) tmp_thy
end
fun check_rhs (_, _, _, rhs, _) =
if Sign.of_sort tmp_thy (rhs, @{sort "domain"}) then ()
else error ("Type not of sort domain: " ^
quote (Syntax.string_of_typ_global tmp_thy rhs))
val _ = map check_rhs doms
val paramT = TFree (param, @{sort domain})
fun mk_tc_eqn (vs, tbind, _, rhs, _) =
(Type (Sign.full_name tmp_thy tbind, map TFree vs), rhs)
val tc_eqns = map mk_tc_eqn doms
fun mk_dom_eqn (vs, tbind, _, rhs, _) =
(mk_appT paramT (Type (Sign.full_name tmp_thy tbind, map TFree vs)), rhs)
val dom_eqns = map mk_dom_eqn doms
val (tyvars, _, _, _, _) = hd doms
val _ = map (fn (tvs, tname, _, _, _) =>
let val full_tname = Sign.full_name tmp_thy tname
in
(case duplicates (op =) (map fst tvs) of
[] =>
if eq_set (op =) (tyvars, tvs) then (full_tname, tvs)
else error ("Mutually recursive domains must have same type parameters")
| dups => error ("Duplicate parameter(s) for domain " ^ Binding.print tname ^
" : " ^ commas dups))
end) doms
val dbinds = map (fn (_, dbind, _, _, _) => dbind) doms
val morphs = map (fn (_, _, _, _, morphs) => morphs) doms
val tcs : typ list = map fst tc_eqns
val param_defl = Free ("d" ^ Library.unprefix "'" param, deflT)
val lhsTs : typ list = map fst dom_eqns
val defl_rec = Free ("t", mk_tupleT (map (K deflT) lhsTs))
val defl_rews = mk_projs (map (fn T => mk_capply (mk_TC T, param_defl)) tcs) defl_rec
fun defl_body (_, _, _, rhsT, _) = defl_of_typ tmp_thy defl_rews rhsT
val functional = Term.lambda defl_rec (mk_tuple (map defl_body doms))
val tfrees = Term.add_tfrees functional []
val frees = map fst (Term.add_frees functional [])
fun get_defl_flags (vs, _, _, _, _) =
let
fun argT v = TFree v
fun mk_d v = "d" ^ Library.unprefix "'" (fst v)
fun mk_p v = "p" ^ Library.unprefix "'" (fst v)
val args = maps (fn v => [(mk_d v, mk_DEFL (argT v)), (mk_p v, mk_LIFTDEFL (argT v))]) vs
val typeTs = map argT (filter (member (op =) tfrees) vs)
val defl_args = map snd (filter (member (op =) frees o fst) args)
in
(typeTs, defl_args)
end
val defl_flagss = map get_defl_flags doms
fun declare_defl_const ((typeTs, defl_args), (_, tbind, _, _, _)) thy =
let
val defl_bind = Binding.suffix_name "_defl" tbind
val defl_type =
map Term.itselfT typeTs ---> map fastype_of defl_args -->> deflT ->> deflT
in
Sign.declare_const_global ((defl_bind, defl_type), NoSyn) thy
end
val (defl_consts, thy) =
fold_map declare_defl_const (defl_flagss ~~ doms) thy
val (_, tmp_thy) =
fold_map declare_defl_const (defl_flagss ~~ doms) tmp_thy
fun mk_defl_term (defl_const, (typeTs, defl_args)) =
let
val type_args = map Logic.mk_type typeTs
in
list_ccomb (list_comb (defl_const, type_args), defl_args @ [param_defl])
end
val defl_terms = map mk_defl_term (defl_consts ~~ defl_flagss)
val defl_rews = map fst defl_rews ~~ defl_terms
fun mk_defl_spec (lhsT, rhsT) =
mk_eqs (defl_of_typ tmp_thy defl_rews lhsT,
defl_of_typ tmp_thy defl_rews rhsT)
val defl_specs = map mk_defl_spec dom_eqns
val defl_binds = map (Binding.suffix_name "_defl") dbinds
val ((defl_apply_thms, defl_unfold_thms, defl_cont_thm), thy) =
add_fixdefs (defl_binds ~~ defl_specs) thy
fun make_tycondef ((vs, tbind, mx, _, _), tc) thy =
let
val spec = (tbind, vs, mx)
val ((full_tname, info), thy) = define_singleton_type spec thy
val newT = #abs_type (#1 info)
val lhs_tfrees = map dest_TFree (snd (dest_Type newT))
val tc_eqn = Logic.mk_equals (tc_const newT, Abs ("", Term.itselfT newT, tc))
val ((_, (_, tc_ldef)), lthy) = thy
|> Class.instantiation ([full_tname], lhs_tfrees, @{sort tycon})
|> Specification.definition NONE [] []
((Binding.prefix_name "tc_" (Binding.suffix_name "_def" tbind), []), tc_eqn)
val ctxt_thy = Proof_Context.init_global (Proof_Context.theory_of lthy)
val tc_def = singleton (Proof_Context.export lthy ctxt_thy) tc_ldef
val thy = lthy
|> Class.prove_instantiation_exit (fn ctxt => Class.intro_classes_tac ctxt [])
val attr = @{attributes [domain_defl_simps]}
|> map (Attrib.attribute_global thy)
val tc_thm = Drule.zero_var_indexes (tc_def RS TC_simp)
val tc_thm_bind = Binding.prefix_name "TC_" tbind
val (_, thy) = thy |> Global_Theory.add_thm ((tc_thm_bind, tc_thm), attr)
in
(tc_def, thy)
end
fun mk_defl_term (defl_const, (typeTs, defl_args)) =
let
val type_args = map Logic.mk_type typeTs
in
list_ccomb (list_comb (defl_const, type_args), defl_args)
end
val defl_terms = map mk_defl_term (defl_consts ~~ defl_flagss)
val (tc_defs, thy) = fold_map make_tycondef (doms ~~ defl_terms) thy
fun mk_DEFL_eq_thm (lhsT, rhsT) =
let
val goal = mk_eqs (mk_DEFL lhsT, mk_DEFL rhsT)
val DEFL_simps = Global_Theory.get_thms thy "domain_defl_simps"
fun tac ctxt =
rewrite_goals_tac ctxt (map mk_meta_eq DEFL_simps @ tc_defs)
THEN TRY (resolve_tac ctxt defl_unfold_thms 1)
in
Goal.prove_global thy [] [] goal (tac o #context)
end
val DEFL_eq_thms = map mk_DEFL_eq_thm dom_eqns
val DEFL_eq_binds = map (Binding.prefix_name "DEFL_eq_") dbinds
val (_, thy) = thy |>
(Global_Theory.add_thms o map Thm.no_attributes)
(DEFL_eq_binds ~~ DEFL_eq_thms)
fun mk_rep_abs ((tbind, _), (lhsT, rhsT)) thy =
let
val rep_bind = Binding.suffix_name "_rep" tbind
val abs_bind = Binding.suffix_name "_abs" tbind
val ((rep_const, rep_def), thy) =
define_const (rep_bind, coerce_const (lhsT, rhsT)) thy
val ((abs_const, abs_def), thy) =
define_const (abs_bind, coerce_const (rhsT, lhsT)) thy
in
(((rep_const, abs_const), (rep_def, abs_def)), thy)
end
val ((rep_abs_consts, rep_abs_defs), thy) = thy
|> fold_map mk_rep_abs (dbinds ~~ morphs ~~ dom_eqns)
|>> ListPair.unzip
fun mk_iso_thms ((tbind, DEFL_eq), (rep_def, abs_def)) thy =
let
fun make thm =
Drule.zero_var_indexes (thm OF [DEFL_eq, abs_def, rep_def])
val rep_iso_thm = make @{thm domain_rep_iso}
val abs_iso_thm = make @{thm domain_abs_iso}
val isodefl_thm = make @{thm isodefl_abs_rep}
val thy = thy
|> snd o add_qualified_thm "rep_iso" (tbind, rep_iso_thm)
|> snd o add_qualified_thm "abs_iso" (tbind, abs_iso_thm)
|> snd o add_qualified_thm "isodefl_abs_rep" (tbind, isodefl_thm)
in
(((rep_iso_thm, abs_iso_thm), isodefl_thm), thy)
end
val ((iso_thms, isodefl_abs_rep_thms), thy) =
thy
|> fold_map mk_iso_thms (dbinds ~~ DEFL_eq_thms ~~ rep_abs_defs)
|>> ListPair.unzip
val iso_infos : Domain_Take_Proofs.iso_info list =
let
fun mk_info (((lhsT, rhsT), (repC, absC)), (rep_iso, abs_iso)) =
{
repT = rhsT,
absT = lhsT,
rep_const = repC,
abs_const = absC,
rep_inverse = rep_iso,
abs_inverse = abs_iso
}
in
map mk_info (dom_eqns ~~ rep_abs_consts ~~ iso_thms)
end
val (map_info, thy) =
define_map_functions (dbinds ~~ iso_infos) thy
val { map_consts, map_apply_thms, map_cont_thm, ...} = map_info
val isodefl_thm =
let
fun unprime a = Library.unprefix "'" a
fun mk_d T = Free ("d" ^ unprime (fst (dest_TFree T)), deflT)
fun mk_p T = Free ("p" ^ unprime (fst (dest_TFree T)), udeflT)
fun mk_f T = Free ("f" ^ unprime (fst (dest_TFree T)), T ->> T)
fun mk_assm t =
case try dest_LIFTDEFL t of
SOME T => mk_trp (isodefl'_const T $ mk_f T $ mk_p T)
| NONE =>
let val T = dest_DEFL t
in mk_trp (isodefl_const T $ mk_f T $ mk_d T) end
fun mk_goal (map_const, ((T, _), defl_term)) =
let
val Ts = argumentTs T
val map_term = list_ccomb (map_const, map mk_f (filter (is_cpo thy) Ts))
val rews1 = map mk_DEFL Ts ~~ map mk_d Ts
val rews2 = map mk_LIFTDEFL Ts ~~ map mk_p Ts
val rews3 = [((mk_TC o snd o dest_appT) T, defl_term)]
val rews = rews1 @ rews2 @ rews3
val defl_term = defl_of_typ thy rews T
in isodefl_const T $ map_term $ defl_term end
val assms = (map mk_assm o snd o hd) defl_flagss
@ [mk_trp (isodefl_const paramT $ mk_f paramT $ mk_d paramT)]
val goals = map mk_goal (map_consts ~~ (dom_eqns ~~ defl_terms))
val goal = mk_trp (foldr1 HOLogic.mk_conj goals)
val adm_rules =
@{thms adm_conj adm_isodefl cont2cont_fst cont2cont_snd cont_id}
val bottom_rules =
@{thms fst_strict snd_strict isodefl_bottom simp_thms}
val tuple_rules =
@{thms split_def fst_conv snd_conv}
val map_ID_thms = Domain_Take_Proofs.get_map_ID_thms thy
val map_ID_simps = map (fn th => th RS sym) map_ID_thms
val isodefl_rules =
@{thms conjI isodefl_ID_DEFL isodefl_LIFTDEFL}
@ isodefl_abs_rep_thms
@ Global_Theory.get_thms thy "domain_isodefl"
in
Goal.prove_global thy [] assms goal (fn {prems, context = ctxt, ...} =>
EVERY
[rewrite_goals_tac ctxt (defl_apply_thms @ map_apply_thms),
resolve_tac ctxt [@{thm cont_parallel_fix_ind} OF [defl_cont_thm, map_cont_thm]] 1,
REPEAT (resolve_tac ctxt adm_rules 1),
simp_tac (put_simpset HOL_basic_ss ctxt addsimps bottom_rules) 1,
simp_tac (put_simpset HOL_basic_ss ctxt addsimps tuple_rules) 1,
simp_tac (put_simpset HOL_basic_ss ctxt addsimps map_ID_simps) 1,
REPEAT (eresolve_tac ctxt @{thms conjE} 1),
REPEAT (resolve_tac ctxt (isodefl_rules @ prems) 1 ORELSE assume_tac ctxt 1)])
end
val isodefl_binds = map (Binding.prefix_name "isodefl_") dbinds
val isodefl_attr = @{attributes [domain_isodefl]}
|> map (Attrib.attribute_global thy)
fun conjuncts [] _ = []
| conjuncts (n::[]) thm = [(n, thm)]
| conjuncts (n::ns) thm = let
val thmL = thm RS @{thm conjunct1}
val thmR = thm RS @{thm conjunct2}
in (n, thmL):: conjuncts ns thmR end
val (isodefl_thms, thy) = thy |>
(Global_Theory.add_thms o map (rpair isodefl_attr o apsnd Drule.zero_var_indexes))
(conjuncts isodefl_binds isodefl_thm)
fun prove_map_ID_thm
(((map_const, (lhsT, _)), DEFL_thm), isodefl_thm) =
let
val Ts = argumentTs lhsT
fun is_cpo T = Sign.of_sort thy (T, @{sort cpo})
val lhs = list_ccomb (map_const, map mk_ID (filter is_cpo Ts))
val goal = mk_eqs (lhs, mk_ID lhsT)
fun tac ctxt = EVERY
[resolve_tac ctxt @{thms isodefl_DEFL_imp_ID} 1,
stac ctxt @{thm DEFL_app} 1,
stac ctxt DEFL_thm 1,
resolve_tac ctxt [isodefl_thm] 1,
REPEAT (resolve_tac ctxt @{thms isodefl_ID_DEFL isodefl_LIFTDEFL} 1)]
in
Goal.prove_global thy [] [] goal (tac o #context)
end
val map_ID_binds = map (Binding.suffix_name "_map_ID") dbinds
val map_ID_thms =
map prove_map_ID_thm
(map_consts ~~ dom_eqns ~~ tc_defs ~~ isodefl_thms)
val (_, thy) = thy |>
(Global_Theory.add_thms o map (rpair [Domain_Take_Proofs.map_ID_add]))
(map_ID_binds ~~ map_ID_thms)
val (take_info, thy) =
Domain_Take_Proofs.define_take_functions
(dbinds ~~ iso_infos) thy
val { take_consts, chain_take_thms, take_0_thms, take_Suc_thms, ...} =
take_info
val lub_take_lemma =
let
val lhs = mk_tuple (map mk_lub take_consts)
fun is_cpo T = Sign.of_sort thy (T, @{sort cpo})
fun mk_map_ID (map_const, (lhsT, _)) =
list_ccomb (map_const, map mk_ID (filter is_cpo (argumentTs lhsT)))
val rhs = mk_tuple (map mk_map_ID (map_consts ~~ dom_eqns))
val goal = mk_trp (mk_eq (lhs, rhs))
val map_ID_thms = Domain_Take_Proofs.get_map_ID_thms thy
val start_rules =
@{thms lub_Pair [symmetric] ch2ch_Pair} @ chain_take_thms
@ @{thms prod.collapse split_def}
@ map_apply_thms @ map_ID_thms
val rules0 =
@{thms iterate_0 Pair_strict} @ take_0_thms
val rules1 =
@{thms iterate_Suc prod_eq_iff fst_conv snd_conv}
@ take_Suc_thms
fun tac ctxt =
EVERY
[simp_tac (put_simpset HOL_basic_ss ctxt addsimps start_rules) 1,
simp_tac (put_simpset HOL_basic_ss ctxt addsimps @{thms fix_def2}) 1,
resolve_tac ctxt @{thms lub_eq} 1,
resolve_tac ctxt @{thms nat.induct} 1,
simp_tac (put_simpset HOL_basic_ss ctxt addsimps rules0) 1,
asm_full_simp_tac (put_simpset beta_ss ctxt addsimps rules1) 1]
in
Goal.prove_global thy [] [] goal (tac o #context)
end
fun prove_lub_take (((dbind, take_const), map_ID_thm), (lhsT, _)) thy =
let
val n = Free ("n", natT)
val goal = mk_eqs (mk_lub (lambda n (take_const $ n)), mk_ID lhsT)
fun tac ctxt =
EVERY
[resolve_tac ctxt @{thms trans} 1,
resolve_tac ctxt [map_ID_thm] 2,
cut_facts_tac [lub_take_lemma] 1,
REPEAT (eresolve_tac ctxt @{thms Pair_inject} 1), assume_tac ctxt 1]
val lub_take_thm = Goal.prove_global thy [] [] goal (tac o #context)
in
add_qualified_thm "lub_take" (dbind, lub_take_thm) thy
end
val (lub_take_thms, thy) =
fold_map prove_lub_take
(dbinds ~~ take_consts ~~ map_ID_thms ~~ dom_eqns) thy
val (take_info2, thy) =
Domain_Take_Proofs.add_lub_take_theorems
(dbinds ~~ iso_infos) take_info lub_take_thms thy
fun fmapU_const T =
let val U = mk_appT udomT T
in Const (@{const_name fmapU}, (udomT ->> udomT) ->> (U ->> U)) end
fun inst_prefunctor (map_const, ((lhsT, _), tbind)) thy =
let
val (_, tyconT) = dest_appT lhsT
val (full_tname, Ts) = dest_Type tyconT
val lhs_tfrees = map dest_TFree Ts
val argTs = filter (is_cpo thy) Ts
val U = mk_appT udomT tyconT
val mapT = map (fn T => T ->> T) argTs -->> (udomT ->> udomT) ->> (U ->> U)
val mapC = Const (fst (dest_Const map_const), mapT)
val fmap_rhs = list_ccomb (mapC, map mk_ID argTs)
val fmap_eqn = Logic.mk_equals (fmapU_const tyconT, fmap_rhs)
val fmap_def_bind = tbind
|> Binding.suffix_name "_def"
|> Binding.prefix_name "fmapU_"
val ((_, (_, fmap_ldef)), lthy) = thy
|> Class.instantiation ([full_tname], lhs_tfrees, @{sort prefunctor})
|> Specification.definition NONE [] [] ((fmap_def_bind, []), fmap_eqn)
val ctxt_thy = Proof_Context.init_global (Proof_Context.theory_of lthy)
val fmap_def = singleton (Proof_Context.export lthy ctxt_thy) fmap_ldef
fun tacf ctxt = EVERY
[Class.intro_classes_tac ctxt [],
rewrite_goals_tac ctxt (fmap_def :: tc_defs),
resolve_tac ctxt isodefl_thms 1,
REPEAT (resolve_tac ctxt @{thms isodefl_ID_DEFL isodefl_LIFTDEFL isodefl_cast} 1)]
val thy = lthy
|> Class.prove_instantiation_exit tacf
in
(fmap_def, thy)
end
val (fmap_defs, thy) = fold_map inst_prefunctor
(map_consts ~~ (dom_eqns ~~ dbinds)) thy
in
((iso_infos, take_info2), thy)
end
type info =
Domain_Take_Proofs.iso_info list * Domain_Take_Proofs.take_induct_info
fun add_arity ((b, sorts, mx), sort) thy : theory =
thy
|> Sign.add_types_global [(b, length sorts, mx)]
|> Axclass.arity_axiomatization (Sign.full_name thy b, sorts, sort)
fun gen_add_tycon
(prep_sort : theory -> 'a -> sort)
(prep_typ : theory -> (string * sort) list -> 'b -> typ)
(arg_sort : bool -> sort)
(raw_specs : (string * (string * 'a) list * binding * mixfix *
(binding * (bool * binding option * 'b) list * mixfix) list) list)
(thy : theory) =
let
val dtnvs0 : (binding * (string * sort) list * mixfix) list =
map (fn (a, vs, dbind, mx, _) =>
(dbind, map (apsnd (prep_sort thy)) vs, mx)) raw_specs
val dtnvs : (binding * typ list * mixfix) list =
let
fun prep_tvar (a, s) = TFree (a, prep_sort thy s)
in
map (fn (a, vs, dbind, mx, _) =>
(dbind, map prep_tvar vs, mx)) raw_specs
end
fun thy_arity (dbind, tvars, mx) =
((dbind, map (snd o dest_TFree) tvars, mx), @{sort tycon})
val tmp_thy = thy
|> fold (add_arity o thy_arity) dtnvs
val dbinds : binding list =
map (fn (_,_,dbind,_,_) => dbind) raw_specs
val raw_rhss :
(binding * (bool * binding option * 'b) list * mixfix) list list =
map (fn (_,_,_,_,cons) => cons) raw_specs
val dtnvs' : (string * typ list) list =
map (fn (dbind, vs, _) => (Sign.full_name thy dbind, vs)) dtnvs
val all_cons = map (Binding.name_of o first) (flat raw_rhss)
val _ =
case duplicates (op =) all_cons of
[] => false | dups => error ("Duplicate constructors: "
^ commas_quote dups)
val all_sels =
(map Binding.name_of o map_filter second o maps second) (flat raw_rhss)
val _ =
case duplicates (op =) all_sels of
[] => false | dups => error("Duplicate selectors: "^commas_quote dups)
fun test_dupl_tvars s =
case duplicates (op =) (map(fst o dest_TFree)s) of
[] => false | dups => error("Duplicate type arguments: "
^commas_quote dups)
val _ = exists test_dupl_tvars (map snd dtnvs')
val param : string * sort =
let val all_params = map #1 raw_specs
in
case distinct (op =) all_params of
[param] => (param, @{sort domain})
| _ => error "Mutually recursive domains must have same type parameter"
end
val sorts : (string * sort) list =
let val all_sorts = map (map dest_TFree o snd) dtnvs'
in
case distinct (eq_set (op =)) all_sorts of
[sorts] => sorts
| _ => error "Mutually recursive domains must have same type parameters"
end
val sorts' : (string * sort) list = param :: sorts
fun check_pcpo (lazy, sel, T) =
let val sort = arg_sort (lazy andalso is_none sel) in
if Sign.of_sort tmp_thy (T, sort) then ()
else error ("Constructor argument type is not of sort " ^
Syntax.string_of_sort_global tmp_thy sort ^ ": " ^
Syntax.string_of_typ_global tmp_thy T)
end
val rec_tab = Domain_Take_Proofs.get_rec_tab thy
fun check_rec _ (T as TFree (v,_)) =
if AList.defined (op =) sorts' v then T
else error ("Free type variable " ^ quote v ^ " on rhs.")
| check_rec no_rec (T as Type (s, Ts)) =
(case AList.lookup (op =) dtnvs' s of
NONE =>
let val no_rec' =
if no_rec = NONE then
if Symtab.defined rec_tab s then NONE else SOME s
else no_rec
in Type (s, map (check_rec no_rec') Ts) end
| SOME typevars =>
if typevars <> Ts
then error ("Recursion of type " ^
quote (Syntax.string_of_typ_global tmp_thy T) ^
" with different arguments")
else (case no_rec of
NONE => T
| SOME c =>
error ("Illegal indirect recursion of type " ^
quote (Syntax.string_of_typ_global tmp_thy T) ^
" under type constructor " ^ quote c)))
| check_rec _ (TVar _) = error "extender:check_rec"
fun prep_arg (lazy, sel, raw_T) =
let
val T = prep_typ tmp_thy sorts raw_T
val _ = check_pcpo (lazy, sel, T)
in (lazy, sel, T) end
fun prep_con (b, args, mx) = (b, map prep_arg args, mx)
fun prep_rhs cons = map prep_con cons
val rhss : (binding * (bool * binding option * typ) list * mixfix) list list =
map prep_rhs raw_rhss
fun mk_arg_typ (lazy, _, T) = if lazy then mk_upT T else T
fun mk_con_typ (_, args, _) =
if null args then oneT else foldr1 mk_sprodT (map mk_arg_typ args)
fun mk_rhs_typ cons = foldr1 mk_ssumT (map mk_con_typ cons)
val repTs : typ list = map mk_rhs_typ rhss
val doms : ((string * sort) list * binding * mixfix * typ * (binding * binding) option) list =
map (fn ((tbind, vs, mx), repT) => (vs, tbind, mx, repT, NONE)) (dtnvs0 ~~ repTs)
val ((iso_infos, take_info), thy) = domain_isomorphism (fst param) doms thy
val (constr_infos, thy) =
thy
|> fold_map (fn ((dbind, cons), info) =>
Domain_Constructors.add_domain_constructors dbind cons info)
(dbinds ~~ rhss ~~ iso_infos)
val (_, thy) =
Domain_Induction.comp_theorems
dbinds take_info constr_infos thy
in
thy
end
fun rep_arg lazy = if lazy then @{sort predomain} else @{sort "domain"}
fun read_sort thy (SOME s) = Syntax.read_sort_global thy s
| read_sort thy NONE = Sign.defaultS thy
fun read_typ thy sorts str =
let
val ctxt = Proof_Context.init_global thy
|> fold (Variable.declare_typ o TFree) sorts
in Syntax.read_typ ctxt str end
fun cert_typ sign sorts raw_T =
let
val T = Type.no_tvars (Sign.certify_typ sign raw_T)
handle TYPE (msg, _, _) => error msg
val sorts' = Term.add_tfreesT T sorts
val _ =
case duplicates (op =) (map fst sorts') of
[] => ()
| dups => error ("Inconsistent sort constraints for " ^ commas dups)
in T end
val add_tycon =
gen_add_tycon (K I) cert_typ rep_arg
val add_tycon_cmd =
gen_add_tycon read_sort read_typ rep_arg
val dest_decl : (bool * binding option * string) parser =
@{keyword "("} |-- Scan.optional (@{keyword "lazy"} >> K true) false --
(Parse.binding >> SOME) -- (@{keyword "::"} |-- Parse.typ) --| @{keyword ")"} >> Scan.triple1
|| @{keyword "("} |-- @{keyword "lazy"} |-- Parse.typ --| @{keyword ")"}
>> (fn t => (true,NONE,t))
|| Parse.typ >> (fn t => (false,NONE,t))
val cons_decl =
Parse.binding -- Scan.repeat dest_decl -- Parse.opt_mixfix
val tycon_decl =
(Parse.type_ident --| @{keyword "⋅"} -- Parse.type_args_constrained --
Parse.binding -- Parse.opt_mixfix) --
(@{keyword "="} |-- Parse.enum1 "|" cons_decl)
val tycons_decl =
Parse.and_list1 tycon_decl
fun mk_tycon
(doms : ((((string * (string * string option) list) * binding) * mixfix) *
((binding * (bool * binding option * string) list) * mixfix) list) list ) =
let
val specs : (string * (string * string option) list * binding * mixfix *
(binding * (bool * binding option * string) list * mixfix) list) list =
map (fn ((((a, vs), t), mx), cons) =>
(a, vs, t, mx, map (fn ((c, ds), mx) => (c, ds, mx)) cons)) doms
in
add_tycon_cmd specs
end
val _ =
Outer_Syntax.command @{command_keyword tycondef}
"define recursive type constructors (HOLCF)"
(tycons_decl >> (Toplevel.theory o mk_tycon))
end
Theory Monad
section ‹Monad Class›
theory Monad
imports Functor
begin
subsection ‹Class definition›
text ‹In Haskell, class \emph{Monad} is defined as follows:›
text_raw ‹
\begin{verbatim}
class Monad m where
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
\end{verbatim}
›
text ‹We formalize class ‹monad› in a manner similar to the
‹functor› class: We fix monomorphic versions of the class
constants, replacing type variables with ‹udom›, and assume
monomorphic versions of the class axioms.›
text ‹Because the monad laws imply the composition rule for ‹fmap›, we declare ‹prefunctor› as the superclass, and separately
prove a subclass relationship with ‹functor›.›
class monad = prefunctor +
fixes returnU :: "udom → udom⋅'a::tycon"
fixes bindU :: "udom⋅'a → (udom → udom⋅'a) → udom⋅'a"
assumes fmapU_eq_bindU:
"⋀f xs. fmapU⋅f⋅xs = bindU⋅xs⋅(Λ x. returnU⋅(f⋅x))"
assumes bindU_returnU:
"⋀f x. bindU⋅(returnU⋅x)⋅f = f⋅x"
assumes bindU_bindU:
"⋀xs f g. bindU⋅(bindU⋅xs⋅f)⋅g = bindU⋅xs⋅(Λ x. bindU⋅(f⋅x)⋅g)"
instance monad ⊆ "functor"
proof
fix f g :: "udom → udom" and xs :: "udom⋅'a"
show "fmapU⋅f⋅(fmapU⋅g⋅xs) = fmapU⋅(Λ x. f⋅(g⋅x))⋅xs"
by (simp add: fmapU_eq_bindU bindU_bindU bindU_returnU)
qed
text ‹As with ‹fmap›, we define the polymorphic ‹return›
and ‹bind› by coercion from the monomorphic ‹returnU› and
‹bindU›.›
definition return :: "'a → 'a⋅'m::monad"
where "return = coerce⋅(returnU :: udom → udom⋅'m)"
definition bind :: "'a⋅'m::monad → ('a → 'b⋅'m) → 'b⋅'m"
where "bind = coerce⋅(bindU :: udom⋅'m → _)"
abbreviation bind_syn :: "'a⋅'m::monad ⇒ ('a → 'b⋅'m) ⇒ 'b⋅'m" (infixl "⤜" 55)
where "m ⤜ f ≡ bind⋅m⋅f"
subsection ‹Naturality of bind and return›
text ‹The three class axioms imply naturality properties of ‹returnU› and ‹bindU›, i.e., that both commute with ‹fmapU›.›
lemma fmapU_returnU [coerce_simp]:
"fmapU⋅f⋅(returnU⋅x) = returnU⋅(f⋅x)"
by (simp add: fmapU_eq_bindU bindU_returnU)
lemma fmapU_bindU [coerce_simp]:
"fmapU⋅f⋅(bindU⋅m⋅k) = bindU⋅m⋅(Λ x. fmapU⋅f⋅(k⋅x))"
by (simp add: fmapU_eq_bindU bindU_bindU)
lemma bindU_fmapU:
"bindU⋅(fmapU⋅f⋅xs)⋅k = bindU⋅xs⋅(Λ x. k⋅(f⋅x))"
by (simp add: fmapU_eq_bindU bindU_returnU bindU_bindU)
subsection ‹Polymorphic versions of class assumptions›
lemma monad_fmap:
fixes xs :: "'a⋅'m::monad" and f :: "'a → 'b"
shows "fmap⋅f⋅xs = xs ⤜ (Λ x. return⋅(f⋅x))"
unfolding bind_def return_def fmap_def
by (simp add: coerce_simp fmapU_eq_bindU bindU_returnU)
lemma monad_left_unit [simp]: "(return⋅x ⤜ f) = (f⋅x)"
unfolding bind_def return_def
by (simp add: coerce_simp bindU_returnU)
lemma bind_bind:
fixes m :: "'a⋅'m::monad"
shows "((m ⤜ f) ⤜ g) = (m ⤜ (Λ x. f⋅x ⤜ g))"
unfolding bind_def
by (simp add: coerce_simp bindU_bindU)
subsection ‹Derived rules›
text ‹The following properties can be derived using only the
abstract monad laws.›
lemma monad_right_unit [simp]: "(m ⤜ return) = m"
apply (subgoal_tac "fmap⋅ID⋅m = m")
apply (simp only: monad_fmap)
apply (simp add: eta_cfun)
apply simp
done
lemma fmap_return: "fmap⋅f⋅(return⋅x) = return⋅(f⋅x)"
by (simp add: monad_fmap)
lemma fmap_bind: "fmap⋅f⋅(bind⋅xs⋅k) = bind⋅xs⋅(Λ x. fmap⋅f⋅(k⋅x))"
by (simp add: monad_fmap bind_bind)
lemma bind_fmap: "bind⋅(fmap⋅f⋅xs)⋅k = bind⋅xs⋅(Λ x. k⋅(f⋅x))"
by (simp add: monad_fmap bind_bind)
text ‹Bind is strict in its first argument, if its second argument
is a strict function.›
lemma bind_strict:
assumes "k⋅⊥ = ⊥" shows "⊥ ⤜ k = ⊥"
proof -
have "⊥ ⤜ k ⊑ return⋅⊥ ⤜ k"
by (intro monofun_cfun below_refl minimal)
thus "⊥ ⤜ k = ⊥"
by (simp add: assms)
qed
lemma congruent_bind:
"(∀m. m ⤜ k1 = m ⤜ k2) = (k1 = k2)"
apply (safe, rule cfun_eqI)
apply (drule_tac x="return⋅x" in spec, simp)
done
subsection ‹Laws for join›
definition join :: "('a⋅'m)⋅'m → 'a⋅'m::monad"
where "join ≡ Λ m. m ⤜ (Λ x. x)"
lemma join_fmap_fmap: "join⋅(fmap⋅(fmap⋅f)⋅xss) = fmap⋅f⋅(join⋅xss)"
by (simp add: join_def monad_fmap bind_bind)
lemma join_return: "join⋅(return⋅xs) = xs"
by (simp add: join_def)
lemma join_fmap_return: "join⋅(fmap⋅return⋅xs) = xs"
by (simp add: join_def monad_fmap eta_cfun bind_bind)
lemma join_fmap_join: "join⋅(fmap⋅join⋅xsss) = join⋅(join⋅xsss)"
by (simp add: join_def monad_fmap bind_bind)
lemma bind_def2: "m ⤜ k = join⋅(fmap⋅k⋅m)"
by (simp add: join_def monad_fmap eta_cfun bind_bind)
subsection ‹Equivalence of monad laws and fmap/join laws›
lemma "(return⋅x ⤜ f) = (f⋅x)"
by (simp only: bind_def2 fmap_return join_return)
lemma "(m ⤜ return) = m"
by (simp only: bind_def2 join_fmap_return)
lemma "((m ⤜ f) ⤜ g) = (m ⤜ (Λ x. f⋅x ⤜ g))"
apply (simp only: bind_def2)
apply (subgoal_tac "join⋅(fmap⋅g⋅(join⋅(fmap⋅f⋅m))) =
join⋅(fmap⋅join⋅(fmap⋅(fmap⋅g)⋅(fmap⋅f⋅m)))")
apply (simp add: fmap_fmap)
apply (simp add: join_fmap_join join_fmap_fmap)
done
subsection ‹Simplification of coercions›
text ‹We configure rewrite rules that push coercions inwards, and
reduce them to coercions on simpler types.›
lemma coerce_return [coerce_simp]:
"COERCE('a⋅'m,'b⋅'m::monad)⋅(return⋅x) = return⋅(COERCE('a,'b)⋅x)"
by (simp add: coerce_functor fmap_return)
lemma coerce_bind [coerce_simp]:
fixes m :: "'a⋅'m::monad" and k :: "'a → 'b⋅'m"
shows "COERCE('b⋅'m,'c⋅'m)⋅(m ⤜ k) = m ⤜ (Λ x. COERCE('b⋅'m,'c⋅'m)⋅(k⋅x))"
by (simp add: coerce_functor fmap_bind)
lemma bind_coerce [coerce_simp]:
fixes m :: "'a⋅'m::monad" and k :: "'b → 'c⋅'m"
shows "COERCE('a⋅'m,'b⋅'m)⋅m ⤜ k = m ⤜ (Λ x. k⋅(COERCE('a,'b)⋅x))"
by (simp add: coerce_functor bind_fmap)
end
Theory Monad_Zero
section ‹Monad-Zero Class›
theory Monad_Zero
imports Monad
begin
class zeroU = tycon +
fixes zeroU :: "udom⋅'a::tycon"
class functor_zero = zeroU + "functor" +
assumes fmapU_zeroU [coerce_simp]:
"fmapU⋅f⋅zeroU = zeroU"
class monad_zero = zeroU + monad +
assumes bindU_zeroU:
"bindU⋅zeroU⋅f = zeroU"
instance monad_zero ⊆ functor_zero
proof
fix f show "fmapU⋅f⋅zeroU = (zeroU :: udom⋅'a)"
unfolding fmapU_eq_bindU
by (rule bindU_zeroU)
qed
definition fzero :: "'a⋅'f::functor_zero"
where "fzero = coerce⋅(zeroU :: udom⋅'f)"
lemma fmap_fzero:
"fmap⋅f⋅(fzero :: 'a⋅'f::functor_zero) = (fzero :: 'b⋅'f)"
unfolding fmap_def fzero_def
by (simp add: coerce_simp)
abbreviation mzero :: "'a⋅'m::monad_zero"
where "mzero ≡ fzero"
lemmas mzero_def = fzero_def [where 'f="'m::monad_zero"] for f
lemmas fmap_mzero = fmap_fzero [where 'f="'m::monad_zero"] for f
lemma bindU_eq_bind: "bindU = bind"
unfolding bind_def by simp
lemma bind_mzero:
"bind⋅(fzero :: 'a⋅'m::monad_zero)⋅k = (mzero :: 'b⋅'m)"
unfolding bind_def mzero_def
by (simp add: coerce_simp bindU_zeroU)
end
Theory Monad_Plus
section ‹Monad-Plus Class›
theory Monad_Plus
imports Monad
begin
hide_const (open) Fixrec.mplus
class plusU = tycon +
fixes plusU :: "udom⋅'a → udom⋅'a → udom⋅'a::tycon"
class functor_plus = plusU + "functor" +
assumes fmapU_plusU [coerce_simp]:
"fmapU⋅f⋅(plusU⋅a⋅b) = plusU⋅(fmapU⋅f⋅a)⋅(fmapU⋅f⋅b)"
assumes plusU_assoc:
"plusU⋅(plusU⋅a⋅b)⋅c = plusU⋅a⋅(plusU⋅b⋅c)"
class monad_plus = plusU + monad +
assumes bindU_plusU:
"bindU⋅(plusU⋅xs⋅ys)⋅k = plusU⋅(bindU⋅xs⋅k)⋅(bindU⋅ys⋅k)"
assumes plusU_assoc':
"plusU⋅(plusU⋅a⋅b)⋅c = plusU⋅a⋅(plusU⋅b⋅c)"
instance monad_plus ⊆ functor_plus
by standard (simp_all only: fmapU_eq_bindU bindU_plusU plusU_assoc')
definition fplus :: "'a⋅'f::functor_plus → 'a⋅'f → 'a⋅'f"
where "fplus = coerce⋅(plusU :: udom⋅'f → _)"
lemma fmap_fplus:
fixes f :: "'a → 'b" and a b :: "'a⋅'f::functor_plus"
shows "fmap⋅f⋅(fplus⋅a⋅b) = fplus⋅(fmap⋅f⋅a)⋅(fmap⋅f⋅b)"
unfolding fmap_def fplus_def
by (simp add: coerce_simp)
lemma fplus_assoc:
fixes a b c :: "'a⋅'f::functor_plus"
shows "fplus⋅(fplus⋅a⋅b)⋅c = fplus⋅a⋅(fplus⋅b⋅c)"
unfolding fplus_def
by (simp add: coerce_simp plusU_assoc)
abbreviation mplus :: "'a⋅'m::monad_plus → 'a⋅'m → 'a⋅'m"
where "mplus ≡ fplus"
lemmas mplus_def = fplus_def [where 'f="'m::monad_plus" for f]
lemmas fmap_mplus = fmap_fplus [where 'f="'m::monad_plus" for f]
lemmas mplus_assoc = fplus_assoc [where 'f="'m::monad_plus" for f]
lemma bind_mplus:
fixes a b :: "'a⋅'m::monad_plus"
shows "bind⋅(mplus⋅a⋅b)⋅k = mplus⋅(bind⋅a⋅k)⋅(bind⋅b⋅k)"
unfolding bind_def mplus_def
by (simp add: coerce_simp bindU_plusU)
lemma join_mplus:
fixes xss yss :: "('a⋅'m)⋅'m::monad_plus"
shows "join⋅(mplus⋅xss⋅yss) = mplus⋅(join⋅xss)⋅(join⋅yss)"
by (simp add: join_def bind_mplus)
end
Theory Monad_Zero_Plus
section ‹Monad-Zero-Plus Class›
theory Monad_Zero_Plus
imports Monad_Zero Monad_Plus
begin
hide_const (open) Fixrec.mplus
class functor_zero_plus = functor_zero + functor_plus +
assumes plusU_zeroU_left:
"plusU⋅zeroU⋅m = m"
assumes plusU_zeroU_right:
"plusU⋅m⋅zeroU = m"
class monad_zero_plus = monad_zero + monad_plus + functor_zero_plus
lemma fplus_fzero_left:
fixes m :: "'a⋅'f::functor_zero_plus"
shows "fplus⋅fzero⋅m = m"
unfolding fplus_def fzero_def
by (simp add: coerce_simp plusU_zeroU_left)
lemma fplus_fzero_right:
fixes m :: "'a⋅'f::functor_zero_plus"
shows "fplus⋅m⋅fzero = m"
unfolding fplus_def fzero_def
by (simp add: coerce_simp plusU_zeroU_right)
lemmas mplus_mzero_left =
fplus_fzero_left [where 'f="'m::monad_zero_plus"] for f
lemmas mplus_mzero_right =
fplus_fzero_right [where 'f="'m::monad_zero_plus"] for f
end
Theory Lazy_List_Monad
section ‹Lazy list monad›
theory Lazy_List_Monad
imports Monad_Zero_Plus
begin
text ‹To illustrate the general process of defining a new type
constructor, we formalize the datatype of lazy lists. Below are the
Haskell datatype definition and class instances.›
text_raw ‹
\begin{verbatim}
data List a = Nil | Cons a (List a)
instance Functor List where
fmap f Nil = Nil
fmap f (Cons x xs) = Cons (f x) (fmap f xs)
instance Monad List where
return x = Cons x Nil
Nil >>= k = Nil
Cons x xs >>= k = mplus (k x) (xs >>= k)
instance MonadZero List where
mzero = Nil
instance MonadPlus List where
mplus Nil ys = ys
mplus (Cons x xs) ys = Cons x (mplus xs ys)
\end{verbatim}
›
subsection ‹Type definition›
text ‹The first step is to register the datatype definition with
‹tycondef›.›
tycondef 'a⋅llist = LNil | LCons (lazy "'a") (lazy "'a⋅llist")
text ‹The ‹tycondef› command generates lots of theorems
automatically, but there are a few more involving ‹coerce› and
‹fmapU› that we still need to prove manually. These proofs could
be automated in a later version of ‹tycondef›.›
lemma coerce_llist_abs [simp]: "coerce⋅(llist_abs⋅x) = llist_abs⋅(coerce⋅x)"
apply (simp add: llist_abs_def coerce_def)
apply (simp add: emb_prj_emb prj_emb_prj DEFL_eq_llist)
done
lemma coerce_LNil [simp]: "coerce⋅LNil = LNil"
unfolding LNil_def by simp
lemma coerce_LCons [simp]: "coerce⋅(LCons⋅x⋅xs) = LCons⋅(coerce⋅x)⋅(coerce⋅xs)"
unfolding LCons_def by simp
lemma fmapU_llist_simps [simp]:
"fmapU⋅f⋅(⊥::udom⋅llist) = ⊥"
"fmapU⋅f⋅LNil = LNil"
"fmapU⋅f⋅(LCons⋅x⋅xs) = LCons⋅(f⋅x)⋅(fmapU⋅f⋅xs)"
unfolding fmapU_llist_def llist_map_def
apply (subst fix_eq, simp)
apply (subst fix_eq, simp add: LNil_def)
apply (subst fix_eq, simp add: LCons_def)
done
subsection ‹Class instances›
text ‹The ‹tycondef› command defines ‹fmapU› for us and
proves a ‹prefunctor› class instance automatically. For the
‹functor› instance we only need to prove the composition law,
which we can do by induction.›
instance llist :: "functor"
proof
fix f g and xs :: "udom⋅llist"
show "fmapU⋅f⋅(fmapU⋅g⋅xs) = fmapU⋅(Λ x. f⋅(g⋅x))⋅xs"
by (induct xs rule: llist.induct) simp_all
qed
text ‹For the other class instances, we need to provide definitions
for a few constants: ‹returnU›, ‹bindU› ‹zeroU›, and
‹plusU›. We can use ordinary commands like ‹definition›
and ‹fixrec› for this purpose. Finally we prove the class
axioms, along with a few helper lemmas, using ordinary proof
procedures like induction.›
instantiation llist :: monad_zero_plus
begin
fixrec plusU_llist :: "udom⋅llist → udom⋅llist → udom⋅llist"
where "plusU_llist⋅LNil⋅ys = ys"
| "plusU_llist⋅(LCons⋅x⋅xs)⋅ys = LCons⋅x⋅(plusU_llist⋅xs⋅ys)"
lemma plusU_llist_strict [simp]: "plusU⋅⊥⋅ys = (⊥::udom⋅llist)"
by fixrec_simp
fixrec bindU_llist :: "udom⋅llist → (udom → udom⋅llist) → udom⋅llist"
where "bindU_llist⋅LNil⋅k = LNil"
| "bindU_llist⋅(LCons⋅x⋅xs)⋅k = plusU⋅(k⋅x)⋅(bindU_llist⋅xs⋅k)"
lemma bindU_llist_strict [simp]: "bindU⋅⊥⋅k = (⊥::udom⋅llist)"
by fixrec_simp
definition zeroU_llist_def:
"zeroU = LNil"
definition returnU_llist_def:
"returnU = (Λ x. LCons⋅x⋅LNil)"
lemma plusU_LNil_right: "plusU⋅xs⋅LNil = xs"
by (induct xs rule: llist.induct) simp_all
lemma plusU_llist_assoc:
fixes xs ys zs :: "udom⋅llist"
shows "plusU⋅(plusU⋅xs⋅ys)⋅zs = plusU⋅xs⋅(plusU⋅ys⋅zs)"
by (induct xs rule: llist.induct) simp_all
lemma bindU_plusU_llist:
fixes xs ys :: "udom⋅llist" shows
"bindU⋅(plusU⋅xs⋅ys)⋅f = plusU⋅(bindU⋅xs⋅f)⋅(bindU⋅ys⋅f)"
by (induct xs rule: llist.induct) (simp_all add: plusU_llist_assoc)
instance proof
fix x :: "udom"
fix f :: "udom → udom"
fix h k :: "udom → udom⋅llist"
fix xs ys zs :: "udom⋅llist"
show "fmapU⋅f⋅xs = bindU⋅xs⋅(Λ x. returnU⋅(f⋅x))"
by (induct xs rule: llist.induct, simp_all add: returnU_llist_def)
show "bindU⋅(returnU⋅x)⋅k = k⋅x"
by (simp add: returnU_llist_def plusU_LNil_right)
show "bindU⋅(bindU⋅xs⋅h)⋅k = bindU⋅xs⋅(Λ x. bindU⋅(h⋅x)⋅k)"
by (induct xs rule: llist.induct)
(simp_all add: bindU_plusU_llist)
show "bindU⋅(plusU⋅xs⋅ys)⋅k = plusU⋅(bindU⋅xs⋅k)⋅(bindU⋅ys⋅k)"
by (induct xs rule: llist.induct)
(simp_all add: plusU_llist_assoc)
show "plusU⋅(plusU⋅xs⋅ys)⋅zs = plusU⋅xs⋅(plusU⋅ys⋅zs)"
by (rule plusU_llist_assoc)
show "bindU⋅zeroU⋅k = zeroU"
by (simp add: zeroU_llist_def)
show "fmapU⋅f⋅(plusU⋅xs⋅ys) = plusU⋅(fmapU⋅f⋅xs)⋅(fmapU⋅f⋅ys)"
by (induct xs rule: llist.induct) simp_all
show "fmapU⋅f⋅zeroU = (zeroU :: udom⋅llist)"
by (simp add: zeroU_llist_def)
show "plusU⋅zeroU⋅xs = xs"
by (simp add: zeroU_llist_def)
show "plusU⋅xs⋅zeroU = xs"
by (simp add: zeroU_llist_def plusU_LNil_right)
qed
end
subsection ‹Transfer properties to polymorphic versions›
text ‹After proving the class instances, there is still one more
step: We must transfer all the list-specific lemmas about the
monomorphic constants (e.g., ‹fmapU› and ‹bindU›) to the
corresponding polymorphic constants (‹fmap› and ‹bind›).
These lemmas primarily consist of the defining equations for each
constant. The polymorphic constants are defined using ‹coerce›,
so the proofs proceed by unfolding the definitions and simplifying
with the ‹coerce_simp› rules.›
lemma fmap_llist_simps [simp]:
"fmap⋅f⋅(⊥::'a⋅llist) = ⊥"
"fmap⋅f⋅LNil = LNil"
"fmap⋅f⋅(LCons⋅x⋅xs) = LCons⋅(f⋅x)⋅(fmap⋅f⋅xs)"
unfolding fmap_def by simp_all
lemma mplus_llist_simps [simp]:
"mplus⋅(⊥::'a⋅llist)⋅ys = ⊥"
"mplus⋅LNil⋅ys = ys"
"mplus⋅(LCons⋅x⋅xs)⋅ys = LCons⋅x⋅(mplus⋅xs⋅ys)"
unfolding mplus_def by simp_all
lemma bind_llist_simps [simp]:
"bind⋅(⊥::'a⋅llist)⋅f = ⊥"
"bind⋅LNil⋅f = LNil"
"bind⋅(LCons⋅x⋅xs)⋅f = mplus⋅(f⋅x)⋅(bind⋅xs⋅f)"
unfolding bind_def mplus_def
by (simp_all add: coerce_simp)
lemma return_llist_def:
"return = (Λ x. LCons⋅x⋅LNil)"
unfolding return_def returnU_llist_def
by (simp add: coerce_simp)
lemma mzero_llist_def:
"mzero = LNil"
unfolding mzero_def zeroU_llist_def
by simp
lemma join_llist_simps [simp]:
"join⋅(⊥::'a⋅llist⋅llist) = ⊥"
"join⋅LNil = LNil"
"join⋅(LCons⋅xs⋅xss) = mplus⋅xs⋅(join⋅xss)"
unfolding join_def by simp_all
end
Theory Maybe_Monad
section ‹Maybe monad›
theory Maybe_Monad
imports Monad_Zero_Plus
begin
subsection ‹Type definition›
tycondef 'a⋅maybe = Nothing | Just (lazy "'a")
lemma coerce_maybe_abs [simp]: "coerce⋅(maybe_abs⋅x) = maybe_abs⋅(coerce⋅x)"
apply (simp add: maybe_abs_def coerce_def)
apply (simp add: emb_prj_emb prj_emb_prj DEFL_eq_maybe)
done
lemma coerce_Nothing [simp]: "coerce⋅Nothing = Nothing"
unfolding Nothing_def by simp
lemma coerce_Just [simp]: "coerce⋅(Just⋅x) = Just⋅(coerce⋅x)"
unfolding Just_def by simp
lemma fmapU_maybe_simps [simp]:
"fmapU⋅f⋅(⊥::udom⋅maybe) = ⊥"
"fmapU⋅f⋅Nothing = Nothing"
"fmapU⋅f⋅(Just⋅x) = Just⋅(f⋅x)"
unfolding fmapU_maybe_def maybe_map_def fix_const
apply simp
apply (simp add: Nothing_def)
apply (simp add: Just_def)
done
subsection ‹Class instance proofs›
instance maybe :: "functor"
apply standard
apply (induct_tac xs rule: maybe.induct, simp_all)
done
instantiation maybe :: "{functor_zero_plus, monad_zero}"
begin
fixrec plusU_maybe :: "udom⋅maybe → udom⋅maybe → udom⋅maybe"
where "plusU_maybe⋅Nothing⋅ys = ys"
| "plusU_maybe⋅(Just⋅x)⋅ys = Just⋅x"
lemma plusU_maybe_strict [simp]: "plusU⋅⊥⋅ys = (⊥::udom⋅maybe)"
by fixrec_simp
fixrec bindU_maybe :: "udom⋅maybe → (udom → udom⋅maybe) → udom⋅maybe"
where "bindU_maybe⋅Nothing⋅k = Nothing"
| "bindU_maybe⋅(Just⋅x)⋅k = k⋅x"
lemma bindU_maybe_strict [simp]: "bindU⋅⊥⋅k = (⊥::udom⋅maybe)"
by fixrec_simp
definition zeroU_maybe_def:
"zeroU = Nothing"
definition returnU_maybe_def:
"returnU = Just"
lemma plusU_Nothing_right: "plusU⋅xs⋅Nothing = xs"
by (induct xs rule: maybe.induct) simp_all
lemma bindU_plusU_maybe:
fixes xs ys :: "udom⋅maybe" shows
"bindU⋅(plusU⋅xs⋅ys)⋅f = plusU⋅(bindU⋅xs⋅f)⋅(bindU⋅ys⋅f)"
apply (induct xs rule: maybe.induct)
apply simp_all
oops
instance proof
fix x :: "udom"
fix f :: "udom → udom"
fix h k :: "udom → udom⋅maybe"
fix xs ys zs :: "udom⋅maybe"
show "fmapU⋅f⋅xs = bindU⋅xs⋅(Λ x. returnU⋅(f⋅x))"
by (induct xs rule: maybe.induct, simp_all add: returnU_maybe_def)
show "bindU⋅(returnU⋅x)⋅k = k⋅x"
by (simp add: returnU_maybe_def plusU_Nothing_right)
show "bindU⋅(bindU⋅xs⋅h)⋅k = bindU⋅xs⋅(Λ x. bindU⋅(h⋅x)⋅k)"
by (induct xs rule: maybe.induct) simp_all
show "plusU⋅(plusU⋅xs⋅ys)⋅zs = plusU⋅xs⋅(plusU⋅ys⋅zs)"
by (induct xs rule: maybe.induct) simp_all
show "bindU⋅zeroU⋅k = zeroU"
by (simp add: zeroU_maybe_def)
show "fmapU⋅f⋅(plusU⋅xs⋅ys) = plusU⋅(fmapU⋅f⋅xs)⋅(fmapU⋅f⋅ys)"
by (induct xs rule: maybe.induct) simp_all
show "fmapU⋅f⋅zeroU = (zeroU :: udom⋅maybe)"
by (simp add: zeroU_maybe_def)
show "plusU⋅zeroU⋅xs = xs"
by (simp add: zeroU_maybe_def)
show "plusU⋅xs⋅zeroU = xs"
by (simp add: zeroU_maybe_def plusU_Nothing_right)
qed
end
subsection ‹Transfer properties to polymorphic versions›
lemma fmap_maybe_simps [simp]:
"fmap⋅f⋅(⊥::'a⋅maybe) = ⊥"
"fmap⋅f⋅Nothing = Nothing"
"fmap⋅f⋅(Just⋅x) = Just⋅(f⋅x)"
unfolding fmap_def by simp_all
lemma fplus_maybe_simps [simp]:
"fplus⋅(⊥::'a⋅maybe)⋅ys = ⊥"
"fplus⋅Nothing⋅ys = ys"
"fplus⋅(Just⋅x)⋅ys = Just⋅x"
unfolding fplus_def by simp_all
lemma fplus_Nothing_right [simp]:
"fplus⋅m⋅Nothing = m"
by (simp add: fplus_def plusU_Nothing_right)
lemma bind_maybe_simps [simp]:
"bind⋅(⊥::'a⋅maybe)⋅f = ⊥"
"bind⋅Nothing⋅f = Nothing"
"bind⋅(Just⋅x)⋅f = f⋅x"
unfolding bind_def fplus_def by simp_all
lemma return_maybe_def: "return = Just"
unfolding return_def returnU_maybe_def
by (simp add: coerce_cfun cfcomp1 eta_cfun)
lemma mzero_maybe_def: "mzero = Nothing"
unfolding mzero_def zeroU_maybe_def
by simp
lemma join_maybe_simps [simp]:
"join⋅(⊥::'a⋅maybe⋅maybe) = ⊥"
"join⋅Nothing = Nothing"
"join⋅(Just⋅xs) = xs"
unfolding join_def by simp_all
subsection ‹Maybe is not in ‹monad_plus››
text ‹
The ‹maybe› type does not satisfy the law ‹bind_mplus›.
›
lemma maybe_counterexample1:
"⟦a = Just⋅x; b = ⊥; k⋅x = Nothing⟧
⟹ fplus⋅a⋅b ⤜ k ≠ fplus⋅(a ⤜ k)⋅(b ⤜ k)"
by simp
lemma maybe_counterexample2:
"⟦a = Just⋅x; b = Just⋅y; k⋅x = Nothing; k⋅y = Just⋅z⟧
⟹ fplus⋅a⋅b ⤜ k ≠ fplus⋅(a ⤜ k)⋅(b ⤜ k)"
by simp
end
Theory Error_Monad
section ‹Error monad›
theory Error_Monad
imports Monad_Plus
begin
subsection ‹Type definition›
tycondef 'a⋅'e error = Err (lazy "'e") | Ok (lazy "'a")
lemma coerce_error_abs [simp]: "coerce⋅(error_abs⋅x) = error_abs⋅(coerce⋅x)"
apply (simp add: error_abs_def coerce_def)
apply (simp add: emb_prj_emb prj_emb_prj DEFL_eq_error)
done
lemma coerce_Err [simp]: "coerce⋅(Err⋅x) = Err⋅(coerce⋅x)"
unfolding Err_def by simp
lemma coerce_Ok [simp]: "coerce⋅(Ok⋅m) = Ok⋅(coerce⋅m)"
unfolding Ok_def by simp
lemma fmapU_error_simps [simp]:
"fmapU⋅f⋅(⊥::udom⋅'a error) = ⊥"
"fmapU⋅f⋅(Err⋅e) = Err⋅e"
"fmapU⋅f⋅(Ok⋅x) = Ok⋅(f⋅x)"
unfolding fmapU_error_def error_map_def fix_const
apply simp
apply (simp add: Err_def)
apply (simp add: Ok_def)
done
subsection ‹Monad class instance›
instantiation error :: ("domain") "{monad, functor_plus}"
begin
definition
"returnU = Ok"
fixrec bindU_error :: "udom⋅'a error → (udom → udom⋅'a error) → udom⋅'a error"
where "bindU_error⋅(Err⋅e)⋅f = Err⋅e"
| "bindU_error⋅(Ok⋅x)⋅f = f⋅x"
lemma bindU_error_strict [simp]: "bindU⋅⊥⋅k = (⊥::udom⋅'a error)"
by fixrec_simp
fixrec plusU_error :: "udom⋅'a error → udom⋅'a error → udom⋅'a error"
where "plusU_error⋅(Err⋅e)⋅f = f"
| "plusU_error⋅(Ok⋅x)⋅f = Ok⋅x"
lemma plusU_error_strict [simp]: "plusU⋅(⊥ :: udom⋅'a error) = ⊥"
by fixrec_simp
instance proof
fix f g :: "udom → udom" and r :: "udom⋅'a error"
show "fmapU⋅f⋅(fmapU⋅g⋅r) = fmapU⋅(Λ x. f⋅(g⋅x))⋅r"
by (induct r rule: error.induct) simp_all
next
fix f :: "udom → udom" and r :: "udom⋅'a error"
show "fmapU⋅f⋅r = bindU⋅r⋅(Λ x. returnU⋅(f⋅x))"
by (induct r rule: error.induct)
(simp_all add: returnU_error_def)
next
fix f :: "udom → udom⋅'a error" and x :: "udom"
show "bindU⋅(returnU⋅x)⋅f = f⋅x"
by (simp add: returnU_error_def)
next
fix r :: "udom⋅'a error" and f g :: "udom → udom⋅'a error"
show "bindU⋅(bindU⋅r⋅f)⋅g = bindU⋅r⋅(Λ x. bindU⋅(f⋅x)⋅g)"
by (induct r rule: error.induct)
simp_all
next
fix f :: "udom → udom" and a b :: "udom⋅'a error"
show "fmapU⋅f⋅(plusU⋅a⋅b) = plusU⋅(fmapU⋅f⋅a)⋅(fmapU⋅f⋅b)"
by (induct a rule: error.induct) simp_all
next
fix a b c :: "udom⋅'a error"
show "plusU⋅(plusU⋅a⋅b)⋅c = plusU⋅a⋅(plusU⋅b⋅c)"
by (induct a rule: error.induct) simp_all
qed
end
subsection ‹Transfer properties to polymorphic versions›
lemma fmap_error_simps [simp]:
"fmap⋅f⋅(⊥::'a⋅'e error) = ⊥"
"fmap⋅f⋅(Err⋅e :: 'a⋅'e error) = Err⋅e"
"fmap⋅f⋅(Ok⋅x :: 'a⋅'e error) = Ok⋅(f⋅x)"
unfolding fmap_def [where 'f="'e error"]
by (simp_all add: coerce_simp)
lemma return_error_def: "return = Ok"
unfolding return_def returnU_error_def
by (simp add: coerce_simp eta_cfun)
lemma bind_error_simps [simp]:
"bind⋅(⊥ :: 'a⋅'e error)⋅f = ⊥"
"bind⋅(Err⋅e :: 'a⋅'e error)⋅f = Err⋅e"
"bind⋅(Ok⋅x :: 'a⋅'e error)⋅f = f⋅x"
unfolding bind_def
by (simp_all add: coerce_simp)
lemma join_error_simps [simp]:
"join⋅⊥ = (⊥ :: 'a⋅'e error)"
"join⋅(Err⋅e) = Err⋅e"
"join⋅(Ok⋅x) = x"
unfolding join_def by simp_all
lemma fplus_error_simps [simp]:
"fplus⋅⊥⋅r = (⊥ :: 'a⋅'e error)"
"fplus⋅(Err⋅e)⋅r = r"
"fplus⋅(Ok⋅x)⋅r = Ok⋅x"
unfolding fplus_def
by (simp_all add: coerce_simp)
end
Theory Writer_Monad
section ‹Writer monad›
theory Writer_Monad
imports Monad
begin
subsection ‹Monoid class›
class monoid = "domain" +
fixes mempty :: "'a"
fixes mappend :: "'a → 'a → 'a"
assumes mempty_left: "⋀ys. mappend⋅mempty⋅ys = ys"
assumes mempty_right: "⋀xs. mappend⋅xs⋅mempty = xs"
assumes mappend_assoc:
"⋀xs ys zs. mappend⋅(mappend⋅xs⋅ys)⋅zs = mappend⋅xs⋅(mappend⋅ys⋅zs)"
subsection ‹Writer monad type›
text ‹Below is the standard Haskell definition of a writer monad
type; it is an isomorphic copy of the lazy pair type \texttt{(a, w)}.
›
text_raw ‹
\begin{verbatim}
newtype Writer w a = Writer { runWriter :: (a, w) }
\end{verbatim}
›
text ‹Since HOLCF does not have a pre-defined lazy pair type, we
will base this formalization on an equivalent, more direct definition:
›
text_raw ‹
\begin{verbatim}
data Writer w a = Writer w a
\end{verbatim}
›
text ‹We can directly translate the above Haskell type definition
using ‹tycondef›. \medskip›
tycondef 'a⋅'w writer = Writer (lazy "'w") (lazy "'a")
lemma coerce_writer_abs [simp]: "coerce⋅(writer_abs⋅x) = writer_abs⋅(coerce⋅x)"
apply (simp add: writer_abs_def coerce_def)
apply (simp add: emb_prj_emb prj_emb_prj DEFL_eq_writer)
done
lemma coerce_Writer [simp]:
"coerce⋅(Writer⋅w⋅x) = Writer⋅(coerce⋅w)⋅(coerce⋅x)"
unfolding Writer_def by simp
lemma fmapU_writer_simps [simp]:
"fmapU⋅f⋅(⊥::udom⋅'w writer) = ⊥"
"fmapU⋅f⋅(Writer⋅w⋅x) = Writer⋅w⋅(f⋅x)"
unfolding fmapU_writer_def writer_map_def fix_const
apply simp
apply (simp add: Writer_def)
done
subsection ‹Class instance proofs›
instance writer :: ("domain") "functor"
proof
fix f g :: "udom → udom" and xs :: "udom⋅'a writer"
show "fmapU⋅f⋅(fmapU⋅g⋅xs) = fmapU⋅(Λ x. f⋅(g⋅x))⋅xs"
by (induct xs rule: writer.induct) simp_all
qed
instantiation writer :: (monoid) monad
begin
fixrec bindU_writer ::
"udom⋅'a writer → (udom → udom⋅'a writer) → udom⋅'a writer"
where "bindU_writer⋅(Writer⋅w⋅x)⋅f =
(case f⋅x of Writer⋅w'⋅y ⇒ Writer⋅(mappend⋅w⋅w')⋅y)"
lemma bindU_writer_strict [simp]: "bindU⋅⊥⋅k = (⊥::udom⋅'a writer)"
by fixrec_simp
definition
"returnU = Writer⋅mempty"
instance proof
fix f :: "udom → udom" and m :: "udom⋅'a writer"
show "fmapU⋅f⋅m = bindU⋅m⋅(Λ x. returnU⋅(f⋅x))"
by (induct m rule: writer.induct)
(simp_all add: returnU_writer_def mempty_right)
next
fix f :: "udom → udom⋅'a writer" and x :: "udom"
show "bindU⋅(returnU⋅x)⋅f = f⋅x"
by (cases "f⋅x" rule: writer.exhaust)
(simp_all add: returnU_writer_def mempty_left)
next
fix m :: "udom⋅'a writer" and f g :: "udom → udom⋅'a writer"
show "bindU⋅(bindU⋅m⋅f)⋅g = bindU⋅m⋅(Λ x. bindU⋅(f⋅x)⋅g)"
apply (induct m rule: writer.induct, simp)
apply (case_tac "f⋅a" rule: writer.exhaust, simp)
apply (case_tac "g⋅aa" rule: writer.exhaust, simp)
apply (simp add: mappend_assoc)
done
qed
end
subsection ‹Transfer properties to polymorphic versions›
lemma fmap_writer_simps [simp]:
"fmap⋅f⋅(⊥::'a⋅'w writer) = ⊥"
"fmap⋅f⋅(Writer⋅w⋅x :: 'a⋅'w writer) = Writer⋅w⋅(f⋅x)"
unfolding fmap_def [where 'f="'w writer"]
by (simp_all add: coerce_simp)
lemma return_writer_def: "return = Writer⋅mempty"
unfolding return_def returnU_writer_def
by (simp add: coerce_simp eta_cfun)
lemma bind_writer_simps [simp]:
"bind⋅(⊥ :: 'a⋅'w::monoid writer)⋅f = ⊥"
"bind⋅(Writer⋅w⋅x :: 'a⋅'w::monoid writer)⋅k =
(case k⋅x of Writer⋅w'⋅y ⇒ Writer⋅(mappend⋅w⋅w')⋅y)"
unfolding bind_def
apply (simp add: coerce_simp)
apply (cases "k⋅x" rule: writer.exhaust)
apply (simp_all add: coerce_simp)
done
lemma join_writer_simps [simp]:
"join⋅⊥ = (⊥ :: 'a⋅'w::monoid writer)"
"join⋅(Writer⋅w⋅(Writer⋅w'⋅x)) = Writer⋅(mappend⋅w⋅w')⋅x"
unfolding join_def by simp_all
subsection ‹Extra operations›
definition tell :: "'w → unit⋅('w::monoid writer)"
where "tell = (Λ w. Writer⋅w⋅())"
end
Theory Binary_Tree_Monad
section ‹Binary tree monad›
theory Binary_Tree_Monad
imports Monad
begin
subsection ‹Type definition›
tycondef 'a⋅btree =
Leaf (lazy "'a") | Node (lazy "'a⋅btree") (lazy "'a⋅btree")
lemma coerce_btree_abs [simp]: "coerce⋅(btree_abs⋅x) = btree_abs⋅(coerce⋅x)"
apply (simp add: btree_abs_def coerce_def)
apply (simp add: emb_prj_emb prj_emb_prj DEFL_eq_btree)
done
lemma coerce_Leaf [simp]: "coerce⋅(Leaf⋅x) = Leaf⋅(coerce⋅x)"
unfolding Leaf_def by simp
lemma coerce_Node [simp]: "coerce⋅(Node⋅xs⋅ys) = Node⋅(coerce⋅xs)⋅(coerce⋅ys)"
unfolding Node_def by simp
lemma fmapU_btree_simps [simp]:
"fmapU⋅f⋅(⊥::udom⋅btree) = ⊥"
"fmapU⋅f⋅(Leaf⋅x) = Leaf⋅(f⋅x)"
"fmapU⋅f⋅(Node⋅xs⋅ys) = Node⋅(fmapU⋅f⋅xs)⋅(fmapU⋅f⋅ys)"
unfolding fmapU_btree_def btree_map_def
apply (subst fix_eq, simp)
apply (subst fix_eq, simp add: Leaf_def)
apply (subst fix_eq, simp add: Node_def)
done
subsection ‹Class instance proofs›
instance btree :: "functor"
apply standard
apply (induct_tac xs rule: btree.induct, simp_all)
done
instantiation btree :: monad
begin
definition
"returnU = Leaf"
fixrec bindU_btree :: "udom⋅btree → (udom → udom⋅btree) → udom⋅btree"
where "bindU_btree⋅(Leaf⋅x)⋅k = k⋅x"
| "bindU_btree⋅(Node⋅xs⋅ys)⋅k =
Node⋅(bindU_btree⋅xs⋅k)⋅(bindU_btree⋅ys⋅k)"
lemma bindU_btree_strict [simp]: "bindU⋅⊥⋅k = (⊥::udom⋅btree)"
by fixrec_simp
instance proof
fix x :: "udom"
fix f :: "udom → udom"
fix h k :: "udom → udom⋅btree"
fix xs :: "udom⋅btree"
show "fmapU⋅f⋅xs = bindU⋅xs⋅(Λ x. returnU⋅(f⋅x))"
by (induct xs rule: btree.induct, simp_all add: returnU_btree_def)
show "bindU⋅(returnU⋅x)⋅k = k⋅x"
by (simp add: returnU_btree_def)
show "bindU⋅(bindU⋅xs⋅h)⋅k = bindU⋅xs⋅(Λ x. bindU⋅(h⋅x)⋅k)"
by (induct xs rule: btree.induct) simp_all
qed
end
subsection ‹Transfer properties to polymorphic versions›
lemma fmap_btree_simps [simp]:
"fmap⋅f⋅(⊥::'a⋅btree) = ⊥"
"fmap⋅f⋅(Leaf⋅x) = Leaf⋅(f⋅x)"
"fmap⋅f⋅(Node⋅xs⋅ys) = Node⋅(fmap⋅f⋅xs)⋅(fmap⋅f⋅ys)"
unfolding fmap_def by simp_all
lemma bind_btree_simps [simp]:
"bind⋅(⊥::'a⋅btree)⋅k = ⊥"
"bind⋅(Leaf⋅x)⋅k = k⋅x"
"bind⋅(Node⋅xs⋅ys)⋅k = Node⋅(bind⋅xs⋅k)⋅(bind⋅ys⋅k)"
unfolding bind_def
by (simp_all add: coerce_simp)
lemma return_btree_def:
"return = Leaf"
unfolding return_def returnU_btree_def
by (simp add: coerce_simp eta_cfun)
lemma join_btree_simps [simp]:
"join⋅(⊥::'a⋅btree⋅btree) = ⊥"
"join⋅(Leaf⋅xs) = xs"
"join⋅(Node⋅xss⋅yss) = Node⋅(join⋅xss)⋅(join⋅yss)"
unfolding join_def by simp_all
end
Theory Lift_Monad
section ‹Lift monad›
theory Lift_Monad
imports Monad
begin
subsection ‹Type definition›
tycondef 'a⋅lifted = Lifted (lazy "'a")
lemma coerce_lifted_abs [simp]: "coerce⋅(lifted_abs⋅x) = lifted_abs⋅(coerce⋅x)"
apply (simp add: lifted_abs_def coerce_def)
apply (simp add: emb_prj_emb prj_emb_prj DEFL_eq_lifted)
done
lemma coerce_Lifted [simp]: "coerce⋅(Lifted⋅x) = Lifted⋅(coerce⋅x)"
unfolding Lifted_def by simp
lemma fmapU_lifted_simps [simp]:
"fmapU⋅f⋅(⊥::udom⋅lifted) = ⊥"
"fmapU⋅f⋅(Lifted⋅x) = Lifted⋅(f⋅x)"
unfolding fmapU_lifted_def lifted_map_def fix_const
apply simp
apply (simp add: Lifted_def)
done
subsection ‹Class instance proofs›
instance lifted :: "functor"
by standard (induct_tac xs rule: lifted.induct, simp_all)
instantiation lifted :: monad
begin
fixrec bindU_lifted :: "udom⋅lifted → (udom → udom⋅lifted) → udom⋅lifted"
where "bindU_lifted⋅(Lifted⋅x)⋅k = k⋅x"
lemma bindU_lifted_strict [simp]: "bindU⋅⊥⋅k = (⊥::udom⋅lifted)"
by fixrec_simp
definition returnU_lifted_def:
"returnU = Lifted"
instance proof
fix x :: "udom"
fix f :: "udom → udom"
fix h k :: "udom → udom⋅lifted"
fix xs :: "udom⋅lifted"
show "fmapU⋅f⋅xs = bindU⋅xs⋅(Λ x. returnU⋅(f⋅x))"
by (induct xs rule: lifted.induct, simp_all add: returnU_lifted_def)
show "bindU⋅(returnU⋅x)⋅k = k⋅x"
by (simp add: returnU_lifted_def)
show "bindU⋅(bindU⋅xs⋅h)⋅k = bindU⋅xs⋅(Λ x. bindU⋅(h⋅x)⋅k)"
by (induct xs rule: lifted.induct) simp_all
qed
end
subsection ‹Transfer properties to polymorphic versions›
lemma fmap_lifted_simps [simp]:
"fmap⋅f⋅(⊥::'a⋅lifted) = ⊥"
"fmap⋅f⋅(Lifted⋅x) = Lifted⋅(f⋅x)"
unfolding fmap_def by simp_all
lemma bind_lifted_simps [simp]:
"bind⋅(⊥::'a⋅lifted)⋅f = ⊥"
"bind⋅(Lifted⋅x)⋅f = f⋅x"
unfolding bind_def by simp_all
lemma return_lifted_def: "return = Lifted"
unfolding return_def returnU_lifted_def
by (simp add: coerce_cfun cfcomp1 eta_cfun)
lemma join_lifted_simps [simp]:
"join⋅(⊥::'a⋅lifted⋅lifted) = ⊥"
"join⋅(Lifted⋅xs) = xs"
unfolding join_def by simp_all
end
Theory State_Transformer
section ‹State monad transformer›
theory State_Transformer
imports Monad_Zero_Plus
begin
text ‹
This version has non-lifted product, and a non-lifted function space.
›
tycondef 'a⋅('f::"functor", 's) stateT =
StateT (runStateT :: "'s → ('a × 's)⋅'f")
lemma coerce_stateT_abs [simp]: "coerce⋅(stateT_abs⋅x) = stateT_abs⋅(coerce⋅x)"
apply (simp add: stateT_abs_def coerce_def)
apply (simp add: emb_prj_emb prj_emb_prj DEFL_eq_stateT)
done
lemma coerce_StateT [simp]: "coerce⋅(StateT⋅k) = StateT⋅(coerce⋅k)"
unfolding StateT_def by simp
lemma stateT_cases [case_names StateT]:
obtains k where "y = StateT⋅k"
proof
show "y = StateT⋅(runStateT⋅y)"
by (cases y, simp_all)
qed
lemma stateT_induct [case_names StateT]:
fixes P :: "'a⋅('f::functor,'s) stateT ⇒ bool"
assumes "⋀k. P (StateT⋅k)"
shows "P y"
by (cases y rule: stateT_cases, simp add: assms)
lemma stateT_eqI:
"(⋀s. runStateT⋅a⋅s = runStateT⋅b⋅s) ⟹ a = b"
apply (cases a rule: stateT_cases)
apply (cases b rule: stateT_cases)
apply (simp add: cfun_eq_iff)
done
lemma runStateT_coerce [simp]:
"runStateT⋅(coerce⋅k)⋅s = coerce⋅(runStateT⋅k⋅s)"
by (induct k rule: stateT_induct, simp)
subsection ‹Functor class instance›
lemma fmapU_StateT [simp]:
"fmapU⋅f⋅(StateT⋅k) =
StateT⋅(Λ s. fmap⋅(Λ(x, s'). (f⋅x, s'))⋅(k⋅s))"
unfolding fmapU_stateT_def stateT_map_def StateT_def
by (subst fix_eq, simp add: cfun_map_def csplit_def prod_map_def)
lemma runStateT_fmapU [simp]:
"runStateT⋅(fmapU⋅f⋅m)⋅s =
fmap⋅(Λ(x, s'). (f⋅x, s'))⋅(runStateT⋅m⋅s)"
by (cases m rule: stateT_cases, simp)
instantiation stateT :: ("functor", "domain") "functor"
begin
instance
apply standard
apply (induct_tac xs rule: stateT_induct)
apply (simp_all add: fmap_fmap ID_def csplit_def)
done
end
subsection ‹Monad class instance›
instantiation stateT :: (monad, "domain") monad
begin
definition returnU_stateT_def:
"returnU = (Λ x. StateT⋅(Λ s. return⋅(x, s)))"
definition bindU_stateT_def:
"bindU = (Λ m k. StateT⋅(Λ s. runStateT⋅m⋅s ⤜ (Λ (x, s'). runStateT⋅(k⋅x)⋅s')))"
lemma bindU_stateT_StateT [simp]:
"bindU⋅(StateT⋅f)⋅k =
StateT⋅(Λ s. f⋅s ⤜ (Λ (x, s'). runStateT⋅(k⋅x)⋅s'))"
unfolding bindU_stateT_def by simp
lemma runStateT_bindU [simp]:
"runStateT⋅(bindU⋅m⋅k)⋅s = runStateT⋅m⋅s ⤜ (Λ (x, s'). runStateT⋅(k⋅x)⋅s')"
unfolding bindU_stateT_def by simp
instance proof
fix f :: "udom → udom" and r :: "udom⋅('a,'b) stateT"
show "fmapU⋅f⋅r = bindU⋅r⋅(Λ x. returnU⋅(f⋅x))"
by (rule stateT_eqI)
(simp add: returnU_stateT_def monad_fmap prod_map_def csplit_def)
next
fix f :: "udom → udom⋅('a,'b) stateT" and x :: "udom"
show "bindU⋅(returnU⋅x)⋅f = f⋅x"
by (rule stateT_eqI)
(simp add: returnU_stateT_def eta_cfun)
next
fix r :: "udom⋅('a,'b) stateT" and f g :: "udom → udom⋅('a,'b) stateT"
show "bindU⋅(bindU⋅r⋅f)⋅g = bindU⋅r⋅(Λ x. bindU⋅(f⋅x)⋅g)"
by (rule stateT_eqI)
(simp add: bind_bind csplit_def)
qed
end
subsection ‹Monad zero instance›
instantiation stateT :: (monad_zero, "domain") monad_zero
begin
definition zeroU_stateT_def:
"zeroU = StateT⋅(Λ s. mzero)"
lemma runStateT_zeroU [simp]:
"runStateT⋅zeroU⋅s = mzero"
unfolding zeroU_stateT_def by simp
instance proof
fix k :: "udom → udom⋅('a,'b) stateT"
show "bindU⋅zeroU⋅k = zeroU"
by (rule stateT_eqI, simp add: bind_mzero)
qed
end
subsection ‹Monad plus instance›
instantiation stateT :: (monad_plus, "domain") monad_plus
begin
definition plusU_stateT_def:
"plusU = (Λ a b. StateT⋅(Λ s. mplus⋅(runStateT⋅a⋅s)⋅(runStateT⋅b⋅s)))"
lemma runStateT_plusU [simp]:
"runStateT⋅(plusU⋅a⋅b)⋅s =
mplus⋅(runStateT⋅a⋅s)⋅(runStateT⋅b⋅s)"
unfolding plusU_stateT_def by simp
instance proof
fix a b :: "udom⋅('a, 'b) stateT" and k :: "udom → udom⋅('a, 'b) stateT"
show "bindU⋅(plusU⋅a⋅b)⋅k = plusU⋅(bindU⋅a⋅k)⋅(bindU⋅b⋅k)"
by (rule stateT_eqI, simp add: bind_mplus)
next
fix a b c :: "udom⋅('a, 'b) stateT"
show "plusU⋅(plusU⋅a⋅b)⋅c = plusU⋅a⋅(plusU⋅b⋅c)"
by (rule stateT_eqI, simp add: mplus_assoc)
qed
end
subsection ‹Monad zero plus instance›
instance stateT :: (monad_zero_plus, "domain") monad_zero_plus
proof
fix m :: "udom⋅('a, 'b) stateT"
show "plusU⋅zeroU⋅m = m"
by (rule stateT_eqI, simp add: mplus_mzero_left)
next
fix m :: "udom⋅('a, 'b) stateT"
show "plusU⋅m⋅zeroU = m"
by (rule stateT_eqI, simp add: mplus_mzero_right)
qed
subsection ‹Transfer properties to polymorphic versions›
lemma coerce_csplit [coerce_simp]:
shows "coerce⋅(csplit⋅f⋅p) = csplit⋅(Λ x y. coerce⋅(f⋅x⋅y))⋅p"
unfolding csplit_def by simp
lemma csplit_coerce [coerce_simp]:
fixes p :: "'a × 'b"
shows "csplit⋅f⋅(COERCE('a × 'b, 'c × 'd)⋅p) =
csplit⋅(Λ x y. f⋅(COERCE('a, 'c)⋅x)⋅(COERCE('b, 'd)⋅y))⋅p"
unfolding coerce_prod csplit_def prod_map_def by simp
lemma fmap_stateT_simps [simp]:
"fmap⋅f⋅(StateT⋅m :: 'a⋅('f::functor,'s) stateT) =
StateT⋅(Λ s. fmap⋅(Λ (x, s'). (f⋅x, s'))⋅(m⋅s))"
unfolding fmap_def [where 'f="('f, 's) stateT"]
by (simp add: coerce_simp eta_cfun)
lemma runStateT_fmap [simp]:
"runStateT⋅(fmap⋅f⋅m)⋅s = fmap⋅(Λ (x, s'). (f⋅x, s'))⋅(runStateT⋅m⋅s)"
by (induct m rule: stateT_induct, simp)
lemma return_stateT_def:
"(return :: _ → 'a⋅('m::monad, 's) stateT) =
(Λ x. StateT⋅(Λ s. return⋅(x, s)))"
unfolding return_def [where 'm="('m, 's) stateT"] returnU_stateT_def
by (simp add: coerce_simp)
lemma bind_stateT_def:
"bind = (Λ m k. StateT⋅(Λ s. runStateT⋅m⋅s ⤜ (Λ (x, s'). runStateT⋅(k⋅x)⋅s')))"
apply (subst bind_def, subst bindU_stateT_def)
apply (simp add: coerce_simp)
apply (simp add: coerce_idem domain_defl_simps monofun_cfun)
apply (simp add: eta_cfun)
done
text "TODO: add ‹coerce_idem› to ‹coerce_simps›, along\010with monotonicity rules for DEFL."
lemma bind_stateT_simps [simp]:
"bind⋅(StateT⋅m :: 'a⋅('m::monad,'s) stateT)⋅k =
StateT⋅(Λ s. m⋅s ⤜ (Λ (x, s'). runStateT⋅(k⋅x)⋅s'))"
unfolding bind_stateT_def by simp
lemma runStateT_bind [simp]:
"runStateT⋅(m ⤜ k)⋅s = runStateT⋅m⋅s ⤜ (Λ (x, s'). runStateT⋅(k⋅x)⋅s')"
unfolding bind_stateT_def by simp
end
Theory Error_Transformer
section ‹Error monad transformer›
theory Error_Transformer
imports Error_Monad
begin
subsection ‹Type definition›
text ‹The error monad transformer is defined in Haskell by composing
the given monad with a standard error monad:›
text_raw ‹
\begin{verbatim}
data Error e a = Err e | Ok a
newtype ErrorT e m a = ErrorT { runErrorT :: m (Error e a) }
\end{verbatim}
›
text ‹We can formalize this definition directly using ‹tycondef›. \medskip›
tycondef 'a⋅('f::"functor",'e::"domain") errorT =
ErrorT (runErrorT :: "('a⋅'e error)⋅'f")
lemma coerce_errorT_abs [simp]: "coerce⋅(errorT_abs⋅x) = errorT_abs⋅(coerce⋅x)"
apply (simp add: errorT_abs_def coerce_def)
apply (simp add: emb_prj_emb prj_emb_prj DEFL_eq_errorT)
done
lemma coerce_ErrorT [simp]: "coerce⋅(ErrorT⋅k) = ErrorT⋅(coerce⋅k)"
unfolding ErrorT_def by simp
lemma errorT_cases [case_names ErrorT]:
obtains k where "y = ErrorT⋅k"
proof
show "y = ErrorT⋅(runErrorT⋅y)"
by (cases y, simp_all)
qed
lemma ErrorT_runErrorT [simp]: "ErrorT⋅(runErrorT⋅m) = m"
by (cases m rule: errorT_cases, simp)
lemma errorT_induct [case_names ErrorT]:
fixes P :: "'a⋅('f::functor,'e) errorT ⇒ bool"
assumes "⋀k. P (ErrorT⋅k)"
shows "P y"
by (cases y rule: errorT_cases, simp add: assms)
lemma errorT_eq_iff:
"a = b ⟷ runErrorT⋅a = runErrorT⋅b"
apply (cases a rule: errorT_cases)
apply (cases b rule: errorT_cases)
apply simp
done
lemma errorT_eqI:
"runErrorT⋅a = runErrorT⋅b ⟹ a = b"
by (simp add: errorT_eq_iff)
lemma runErrorT_coerce [simp]:
"runErrorT⋅(coerce⋅k) = coerce⋅(runErrorT⋅k)"
by (induct k rule: errorT_induct, simp)
subsection ‹Functor class instance›
lemma fmap_error_def: "fmap = error_map⋅ID"
apply (rule cfun_eqI, rename_tac f)
apply (rule cfun_eqI, rename_tac x)
apply (case_tac x rule: error.exhaust, simp_all)
apply (simp add: error_map_def fix_const)
apply (simp add: error_map_def fix_const Err_def)
apply (simp add: error_map_def fix_const Ok_def)
done
lemma fmapU_ErrorT [simp]:
"fmapU⋅f⋅(ErrorT⋅m) = ErrorT⋅(fmap⋅(fmap⋅f)⋅m)"
unfolding fmapU_errorT_def errorT_map_def fmap_error_def fix_const ErrorT_def
by simp
lemma runErrorT_fmapU [simp]:
"runErrorT⋅(fmapU⋅f⋅m) = fmap⋅(fmap⋅f)⋅(runErrorT⋅m)"
by (induct m rule: errorT_induct) simp
instance errorT :: ("functor", "domain") "functor"
proof
fix f g and xs :: "udom⋅('a, 'b) errorT"
show "fmapU⋅f⋅(fmapU⋅g⋅xs) = fmapU⋅(Λ x. f⋅(g⋅x))⋅xs"
apply (induct xs rule: errorT_induct)
apply (simp add: fmap_fmap eta_cfun)
done
qed
subsection ‹Transfer properties to polymorphic versions›
lemma fmap_ErrorT [simp]:
fixes f :: "'a → 'b" and m :: "'a⋅'e error⋅('m::functor)"
shows "fmap⋅f⋅(ErrorT⋅m) = ErrorT⋅(fmap⋅(fmap⋅f)⋅m)"
unfolding fmap_def [where 'f="('m,'e) errorT"]
by (simp_all add: coerce_simp eta_cfun)
lemma runErrorT_fmap [simp]:
fixes f :: "'a → 'b" and m :: "'a⋅('m::functor,'e) errorT"
shows "runErrorT⋅(fmap⋅f⋅m) = fmap⋅(fmap⋅f)⋅(runErrorT⋅m)"
using fmap_ErrorT [of f "runErrorT⋅m"]
by simp
lemma errorT_fmap_strict [simp]:
shows "fmap⋅f⋅(⊥::'a⋅('m::monad,'e) errorT) = ⊥"
by (simp add: errorT_eq_iff fmap_strict)
subsection ‹Monad operations›
text ‹The error monad transformer does not yield a monad in the
usual sense: We cannot prove a ‹monad› class instance, because
type ‹'a⋅('m,'e) errorT› contains values that break the monad
laws. However, it turns out that such values are inaccessible: The
monad laws are satisfied by all values constructible from the abstract
operations.›
text ‹To explore the properties of the error monad transformer
operations, we define them all as non-overloaded functions. \medskip
›
definition unitET :: "'a → 'a⋅('m::monad,'e) errorT"
where "unitET = (Λ x. ErrorT⋅(return⋅(Ok⋅x)))"
definition bindET :: "'a⋅('m::monad,'e) errorT →
('a → 'b⋅('m,'e) errorT) → 'b⋅('m,'e) errorT"
where "bindET = (Λ m k. ErrorT⋅(bind⋅(runErrorT⋅m)⋅
(Λ n. case n of Err⋅e ⇒ return⋅(Err⋅e) | Ok⋅x ⇒ runErrorT⋅(k⋅x))))"
definition liftET :: "'a⋅'m::monad → 'a⋅('m,'e) errorT"
where "liftET = (Λ m. ErrorT⋅(fmap⋅Ok⋅m))"
definition throwET :: "'e → 'a⋅('m::monad,'e) errorT"
where "throwET = (Λ e. ErrorT⋅(return⋅(Err⋅e)))"
definition catchET :: "'a⋅('m::monad,'e) errorT →
('e → 'a⋅('m,'e) errorT) → 'a⋅('m,'e) errorT"
where "catchET = (Λ m h. ErrorT⋅(bind⋅(runErrorT⋅m)⋅(Λ n. case n of
Err⋅e ⇒ runErrorT⋅(h⋅e) | Ok⋅x ⇒ return⋅(Ok⋅x))))"
definition fmapET :: "('a → 'b) →
'a⋅('m::monad,'e) errorT → 'b⋅('m,'e) errorT"
where "fmapET = (Λ f m. bindET⋅m⋅(Λ x. unitET⋅(f⋅x)))"
lemma runErrorT_unitET [simp]:
"runErrorT⋅(unitET⋅x) = return⋅(Ok⋅x)"
unfolding unitET_def by simp
lemma runErrorT_bindET [simp]:
"runErrorT⋅(bindET⋅m⋅k) = bind⋅(runErrorT⋅m)⋅
(Λ n. case n of Err⋅e ⇒ return⋅(Err⋅e) | Ok⋅x ⇒ runErrorT⋅(k⋅x))"
unfolding bindET_def by simp
lemma runErrorT_liftET [simp]:
"runErrorT⋅(liftET⋅m) = fmap⋅Ok⋅m"
unfolding liftET_def by simp
lemma runErrorT_throwET [simp]:
"runErrorT⋅(throwET⋅e) = return⋅(Err⋅e)"
unfolding throwET_def by simp
lemma runErrorT_catchET [simp]:
"runErrorT⋅(catchET⋅m⋅h) =
bind⋅(runErrorT⋅m)⋅(Λ n. case n of
Err⋅e ⇒ runErrorT⋅(h⋅e) | Ok⋅x ⇒ return⋅(Ok⋅x))"
unfolding catchET_def by simp
lemma runErrorT_fmapET [simp]:
"runErrorT⋅(fmapET⋅f⋅m) =
bind⋅(runErrorT⋅m)⋅(Λ n. case n of
Err⋅e ⇒ return⋅(Err⋅e) | Ok⋅x ⇒ return⋅(Ok⋅(f⋅x)))"
unfolding fmapET_def by simp
subsection ‹Laws›
lemma bindET_unitET [simp]:
"bindET⋅(unitET⋅x)⋅k = k⋅x"
by (rule errorT_eqI, simp)
lemma catchET_unitET [simp]:
"catchET⋅(unitET⋅x)⋅h = unitET⋅x"
by (rule errorT_eqI, simp)
lemma catchET_throwET [simp]:
"catchET⋅(throwET⋅e)⋅h = h⋅e"
by (rule errorT_eqI, simp)
lemma liftET_return:
"liftET⋅(return⋅x) = unitET⋅x"
by (rule errorT_eqI, simp add: fmap_return)
lemma liftET_bind:
"liftET⋅(bind⋅m⋅k) = bindET⋅(liftET⋅m)⋅(liftET oo k)"
by (rule errorT_eqI, simp add: fmap_bind bind_fmap)
lemma bindET_throwET:
"bindET⋅(throwET⋅e)⋅k = throwET⋅e"
by (rule errorT_eqI, simp)
lemma bindET_bindET:
"bindET⋅(bindET⋅m⋅h)⋅k = bindET⋅m⋅(Λ x. bindET⋅(h⋅x)⋅k)"
apply (rule errorT_eqI)
apply simp
apply (simp add: bind_bind)
apply (rule cfun_arg_cong)
apply (rule cfun_eqI, simp)
apply (case_tac x)
apply (simp add: bind_strict)
apply simp
apply simp
done
lemma fmapET_fmapET:
"fmapET⋅f⋅(fmapET⋅g⋅m) = fmapET⋅(Λ x. f⋅(g⋅x))⋅m"
by (simp add: fmapET_def bindET_bindET)
text ‹Right unit monad law is not satisfied in general.›
lemma bindET_unitET_right_counterexample:
fixes m :: "'a⋅('m::monad,'e) errorT"
assumes "m = ErrorT⋅(return⋅⊥)"
assumes "return⋅⊥ ≠ (⊥ :: ('a⋅'e error)⋅'m)"
shows "bindET⋅m⋅unitET ≠ m"
by (simp add: errorT_eq_iff assms)
text ‹Right unit is satisfied for inner monads with strict return.›
lemma bindET_unitET_right_restricted:
fixes m :: "'a⋅('m::monad,'e) errorT"
assumes "return⋅⊥ = (⊥ :: ('a⋅'e error)⋅'m)"
shows "bindET⋅m⋅unitET = m"
unfolding errorT_eq_iff
apply simp
apply (rule trans [OF _ monad_right_unit])
apply (rule cfun_arg_cong)
apply (rule cfun_eqI)
apply (case_tac x, simp_all add: assms)
done
subsection ‹Error monad transformer invariant›
text ‹This inductively-defined invariant is supposed to represent
the set of all values constructible using the standard ‹errorT›
operations.›
inductive invar :: "'a⋅('m::monad, 'e) errorT ⇒ bool"
where invar_bottom: "invar ⊥"
| invar_lub: "⋀Y. ⟦chain Y; ⋀i. invar (Y i)⟧ ⟹ invar (⨆i. Y i)"
| invar_unitET: "⋀x. invar (unitET⋅x)"
| invar_bindET: "⋀m k. ⟦invar m; ⋀x. invar (k⋅x)⟧ ⟹ invar (bindET⋅m⋅k)"
| invar_throwET: "⋀e. invar (throwET⋅e)"
| invar_catchET: "⋀m h. ⟦invar m; ⋀e. invar (h⋅e)⟧ ⟹ invar (catchET⋅m⋅h)"
| invar_liftET: "⋀m. invar (liftET⋅m)"
text ‹Right unit is satisfied for arguments built from standard functions.›
lemma bindET_unitET_right_invar:
assumes "invar m"
shows "bindET⋅m⋅unitET = m"
using assms
apply (induct set: invar)
apply (rule errorT_eqI, simp add: bind_strict)
apply (rule admD, simp, assumption, assumption)
apply (rule errorT_eqI, simp)
apply (simp add: errorT_eq_iff bind_bind)
apply (rule cfun_arg_cong, rule cfun_eqI, simp)
apply (case_tac x, simp add: bind_strict, simp, simp)
apply (rule errorT_eqI, simp)
apply (simp add: errorT_eq_iff bind_bind)
apply (rule cfun_arg_cong, rule cfun_eqI, simp)
apply (case_tac x, simp add: bind_strict, simp, simp)
apply (rule errorT_eqI, simp add: monad_fmap bind_bind)
done
text ‹Monad-fmap is satisfied for arguments built from standard functions.›
lemma errorT_monad_fmap_invar:
fixes f :: "'a → 'b" and m :: "'a⋅('m::monad,'e) errorT"
assumes "invar m"
shows "fmap⋅f⋅m = bindET⋅m⋅(Λ x. unitET⋅(f⋅x))"
using assms
apply (induct set: invar)
apply (rule errorT_eqI, simp add: bind_strict fmap_strict)
apply (rule admD, simp, assumption, assumption)
apply (rule errorT_eqI, simp add: fmap_return)
apply (simp add: errorT_eq_iff bind_bind fmap_bind)
apply (rule cfun_arg_cong, rule cfun_eqI, simp)
apply (case_tac x)
apply (simp add: bind_strict fmap_strict)
apply (simp add: fmap_return)
apply simp
apply (rule errorT_eqI, simp add: fmap_return)
apply (simp add: errorT_eq_iff bind_bind fmap_bind)
apply (rule cfun_arg_cong, rule cfun_eqI, simp)
apply (case_tac x)
apply (simp add: bind_strict fmap_strict)
apply simp
apply (simp add: fmap_return)
apply (rule errorT_eqI, simp add: monad_fmap bind_bind return_error_def)
done
subsection ‹Invariant expressed as a deflation›
text ‹We can also define an invariant in a more semantic way, as the
set of fixed-points of a deflation.›
definition invar' :: "'a⋅('m::monad, 'e) errorT ⇒ bool"
where "invar' m ⟷ fmapET⋅ID⋅m = m"
text ‹All standard operations preserve the invariant.›
lemma invar'_unitET: "invar' (unitET⋅x)"
unfolding invar'_def by (simp add: fmapET_def)
lemma invar'_fmapET: "invar' m ⟹ invar' (fmapET⋅f⋅m)"
unfolding invar'_def
by (erule subst, simp add: fmapET_def bindET_bindET eta_cfun)
lemma invar'_bindET: "⟦invar' m; ⋀x. invar' (k⋅x)⟧ ⟹ invar' (bindET⋅m⋅k)"
unfolding invar'_def
by (simp add: fmapET_def bindET_bindET eta_cfun)
lemma invar'_throwET: "invar' (throwET⋅e)"
unfolding invar'_def by (simp add: fmapET_def bindET_throwET eta_cfun)
lemma invar'_catchET: "⟦invar' m; ⋀e. invar' (h⋅e)⟧ ⟹ invar' (catchET⋅m⋅h)"
unfolding invar'_def
apply (simp add: fmapET_def eta_cfun)
apply (rule errorT_eqI)
apply (simp add: bind_bind eta_cfun)
apply (rule cfun_arg_cong)
apply (rule cfun_eqI)
apply (case_tac x)
apply (simp add: bind_strict)
apply simp
apply (drule_tac x=e in meta_spec)
apply (erule_tac t="h⋅e" in subst) back
apply (simp add: eta_cfun)
apply simp
done
lemma invar'_liftET: "invar' (liftET⋅m)"
unfolding invar'_def
apply (simp add: fmapET_def errorT_eq_iff)
apply (simp add: monad_fmap bind_bind)
done
lemma invar'_bottom: "invar' ⊥"
unfolding invar'_def fmapET_def
by (simp add: errorT_eq_iff bind_strict)
lemma adm_invar': "adm invar'"
unfolding invar'_def [abs_def] by simp
text ‹All monad laws are preserved by values satisfying the invariant.›
lemma bindET_fmapET_unitET:
shows "bindET⋅(fmapET⋅f⋅m)⋅unitET = fmapET⋅f⋅m"
by (simp add: fmapET_def bindET_bindET)
lemma invar'_right_unit: "invar' m ⟹ bindET⋅m⋅unitET = m"
unfolding invar'_def by (erule subst, rule bindET_fmapET_unitET)
lemma invar'_monad_fmap:
"invar' m ⟹ fmapET⋅f⋅m = bindET⋅m⋅(Λ x. unitET⋅(f⋅x))"
unfolding invar'_def by (erule subst, simp add: errorT_eq_iff)
lemma invar'_bind_assoc:
"⟦invar' m; ⋀x. invar' (f⋅x); ⋀y. invar' (g⋅y)⟧
⟹ bindET⋅(bindET⋅m⋅f)⋅g = bindET⋅m⋅(Λ x. bindET⋅(f⋅x)⋅g)"
by (rule bindET_bindET)
end
Theory Writer_Transformer
section ‹Writer monad transformer›
theory Writer_Transformer
imports Writer_Monad
begin
subsection ‹Type definition›
text ‹Below is the standard Haskell definition of a writer monad
transformer:›
text_raw ‹
\begin{verbatim}
newtype WriterT w m a = WriterT { runWriterT :: m (a, w) }
\end{verbatim}
›
text ‹In this development, since a lazy pair type is not pre-defined
in HOLCF, we will use an equivalent formulation in terms of our
previous \texttt{Writer} type:›
text_raw ‹
\begin{verbatim}
data Writer w a = Writer w a
newtype WriterT w m a = WriterT { runWriterT :: m (Writer w a) }
\end{verbatim}
›
text ‹We can translate this definition directly into HOLCF using
‹tycondef›. \medskip›
tycondef 'a⋅('m::"functor",'w) writerT =
WriterT (runWriterT :: "('a⋅'w writer)⋅'m")
lemma coerce_writerT_abs [simp]:
"coerce⋅(writerT_abs⋅x) = writerT_abs⋅(coerce⋅x)"
apply (simp add: writerT_abs_def coerce_def)
apply (simp add: emb_prj_emb prj_emb_prj DEFL_eq_writerT)
done
lemma coerce_WriterT [simp]: "coerce⋅(WriterT⋅k) = WriterT⋅(coerce⋅k)"
unfolding WriterT_def by simp
lemma writerT_cases [case_names WriterT]:
obtains k where "y = WriterT⋅k"
proof
show "y = WriterT⋅(runWriterT⋅y)"
by (cases y, simp_all)
qed
lemma WriterT_runWriterT [simp]: "WriterT⋅(runWriterT⋅m) = m"
by (cases m rule: writerT_cases, simp)
lemma writerT_induct [case_names WriterT]:
fixes P :: "'a⋅('f::functor,'e) writerT ⇒ bool"
assumes "⋀k. P (WriterT⋅k)"
shows "P y"
by (cases y rule: writerT_cases, simp add: assms)
lemma writerT_eq_iff:
"a = b ⟷ runWriterT⋅a = runWriterT⋅b"
apply (cases a rule: writerT_cases)
apply (cases b rule: writerT_cases)
apply simp
done
lemma writerT_below_iff:
"a ⊑ b ⟷ runWriterT⋅a ⊑ runWriterT⋅b"
apply (cases a rule: writerT_cases)
apply (cases b rule: writerT_cases)
apply simp
done
lemma writerT_eqI:
"runWriterT⋅a = runWriterT⋅b ⟹ a = b"
by (simp add: writerT_eq_iff)
lemma writerT_belowI:
"runWriterT⋅a ⊑ runWriterT⋅b ⟹ a ⊑ b"
by (simp add: writerT_below_iff)
lemma runWriterT_coerce [simp]:
"runWriterT⋅(coerce⋅k) = coerce⋅(runWriterT⋅k)"
by (induct k rule: writerT_induct, simp)
subsection ‹Functor class instance›
lemma fmap_writer_def: "fmap = writer_map⋅ID"
apply (rule cfun_eqI, rename_tac f)
apply (rule cfun_eqI, rename_tac x)
apply (case_tac x rule: writer.exhaust, simp_all)
apply (simp add: writer_map_def fix_const)
apply (simp add: writer_map_def fix_const Writer_def)
done
lemma fmapU_WriterT [simp]:
"fmapU⋅f⋅(WriterT⋅m) = WriterT⋅(fmap⋅(fmap⋅f)⋅m)"
unfolding fmapU_writerT_def writerT_map_def fmap_writer_def fix_const
WriterT_def by simp
lemma runWriterT_fmapU [simp]:
"runWriterT⋅(fmapU⋅f⋅m) = fmap⋅(fmap⋅f)⋅(runWriterT⋅m)"
by (induct m rule: writerT_induct) simp
instance writerT :: ("functor", "domain") "functor"
proof
fix f g :: "udom → udom" and xs :: "udom⋅('a,'b) writerT"
show "fmapU⋅f⋅(fmapU⋅g⋅xs) = fmapU⋅(Λ x. f⋅(g⋅x))⋅xs"
apply (induct xs rule: writerT_induct)
apply (simp add: fmap_fmap eta_cfun)
done
qed
subsection ‹Monad operations›
text ‹The writer monad transformer does not yield a monad in the
usual sense: We cannot prove a ‹monad› class instance, because
type ‹'a⋅('m,'w) writerT› contains values that break the monad
laws. However, it turns out that such values are inaccessible: The
monad laws are satisfied by all values constructible from the abstract
operations.›
text ‹To explore the properties of the writer monad transformer
operations, we define them all as non-overloaded functions. \medskip
›
definition unitWT :: "'a → 'a⋅('m::monad,'w::monoid) writerT"
where "unitWT = (Λ x. WriterT⋅(return⋅(Writer⋅mempty⋅x)))"
definition bindWT :: "'a⋅('m::monad,'w::monoid) writerT → ('a → 'b⋅('m,'w) writerT) → 'b⋅('m,'w) writerT"
where "bindWT = (Λ m k. WriterT⋅(bind⋅(runWriterT⋅m)⋅
(Λ(Writer⋅w⋅x). bind⋅(runWriterT⋅(k⋅x))⋅(Λ(Writer⋅w'⋅y).
return⋅(Writer⋅(mappend⋅w⋅w')⋅y)))))"
definition liftWT :: "'a⋅'m → 'a⋅('m::monad,'w::monoid) writerT"
where "liftWT = (Λ m. WriterT⋅(fmap⋅(Writer⋅mempty)⋅m))"
definition tellWT :: "'a → 'w → 'a⋅('m::monad,'w::monoid) writerT"
where "tellWT = (Λ x w. WriterT⋅(return⋅(Writer⋅w⋅x)))"
definition fmapWT :: "('a → 'b) → 'a⋅('m::monad,'w::monoid) writerT → 'b⋅('m,'w) writerT"
where "fmapWT = (Λ f m. bindWT⋅m⋅(Λ x. unitWT⋅(f⋅x)))"
lemma runWriterT_fmap [simp]:
"runWriterT⋅(fmap⋅f⋅m) = fmap⋅(fmap⋅f)⋅(runWriterT⋅m)"
by (subst fmap_def, simp add: coerce_simp eta_cfun)
lemma runWriterT_unitWT [simp]:
"runWriterT⋅(unitWT⋅x) = return⋅(Writer⋅mempty⋅x)"
unfolding unitWT_def by simp
lemma runWriterT_bindWT [simp]:
"runWriterT⋅(bindWT⋅m⋅k) = bind⋅(runWriterT⋅m)⋅
(Λ(Writer⋅w⋅x). bind⋅(runWriterT⋅(k⋅x))⋅(Λ(Writer⋅w'⋅y).
return⋅(Writer⋅(mappend⋅w⋅w')⋅y)))"
unfolding bindWT_def by simp
lemma runWriterT_liftWT [simp]:
"runWriterT⋅(liftWT⋅m) = fmap⋅(Writer⋅mempty)⋅m"
unfolding liftWT_def by simp
lemma runWriterT_tellWT [simp]:
"runWriterT⋅(tellWT⋅x⋅w) = return⋅(Writer⋅w⋅x)"
unfolding tellWT_def by simp
lemma runWriterT_fmapWT [simp]:
"runWriterT⋅(fmapWT⋅f⋅m) =
runWriterT⋅m ⤜ (Λ (Writer⋅w⋅x). return⋅(Writer⋅w⋅(f⋅x)))"
by (simp add: fmapWT_def bindWT_def mempty_right)
subsection ‹Laws›
text ‹The ‹liftWT› function maps ‹return› and
‹bind› on the inner monad to ‹unitWT› and ‹bindWT›, as expected. \medskip›
lemma liftWT_return:
"liftWT⋅(return⋅x) = unitWT⋅x"
by (rule writerT_eqI, simp add: fmap_return)
lemma liftWT_bind:
"liftWT⋅(bind⋅m⋅k) = bindWT⋅(liftWT⋅m)⋅(liftWT oo k)"
by (rule writerT_eqI)
(simp add: monad_fmap bind_bind mempty_left)
text ‹The composition rule holds unconditionally for fmap. The fmap
function also interacts as expected with unit and bind. \medskip›
lemma fmapWT_fmapWT:
"fmapWT⋅f⋅(fmapWT⋅g⋅m) = fmapWT⋅(Λ x. f⋅(g⋅x))⋅m"
apply (simp add: writerT_eq_iff bind_bind)
apply (rule cfun_arg_cong, rule cfun_eqI, simp)
apply (case_tac x, simp add: bind_strict, simp add: mempty_right)
done
lemma fmapWT_unitWT:
"fmapWT⋅f⋅(unitWT⋅x) = unitWT⋅(f⋅x)"
by (simp add: writerT_eq_iff mempty_right)
lemma fmapWT_bindWT:
"fmapWT⋅f⋅(bindWT⋅m⋅k) = bindWT⋅m⋅(Λ x. fmapWT⋅f⋅(k⋅x))"
apply (simp add: writerT_eq_iff bind_bind)
apply (rule cfun_arg_cong, rule cfun_eqI, rename_tac x, simp)
apply (case_tac x, simp add: bind_strict, simp add: bind_bind)
apply (rule cfun_arg_cong, rule cfun_eqI, rename_tac y, simp)
apply (case_tac y, simp add: bind_strict, simp add: mempty_right)
done
lemma bindWT_fmapWT:
"bindWT⋅(fmapWT⋅f⋅m)⋅k = bindWT⋅m⋅(Λ x. k⋅(f⋅x))"
apply (simp add: writerT_eq_iff bind_bind)
apply (rule cfun_arg_cong, rule cfun_eqI, rename_tac x, simp)
apply (case_tac x, simp add: bind_strict, simp add: mempty_right)
done
text ‹The left unit monad law is not satisfied in general. \medskip›
lemma bindWT_unitWT_counterexample:
fixes k :: "'a → 'b⋅('m::monad,'w::monoid) writerT"
assumes 1: "k⋅x = WriterT⋅(return⋅⊥)"
assumes 2: "return⋅⊥ ≠ (⊥ :: ('b⋅'w writer)⋅'m::monad)"
shows "bindWT⋅(unitWT⋅x)⋅k ≠ k⋅x"
by (simp add: writerT_eq_iff mempty_left assms)
text ‹However, left unit is satisfied for inner monads with a strict
‹return› function.›
lemma bindWT_unitWT_restricted:
fixes k :: "'a → 'b⋅('m::monad,'w::monoid) writerT"
assumes "return⋅⊥ = (⊥ :: ('b⋅'w writer)⋅'m)"
shows "bindWT⋅(unitWT⋅x)⋅k = k⋅x"
unfolding writerT_eq_iff
apply (simp add: mempty_left)
apply (rule trans [OF _ monad_right_unit])
apply (rule cfun_arg_cong)
apply (rule cfun_eqI)
apply (case_tac x, simp_all add: assms)
done
text ‹The associativity of ‹bindWT› holds
unconditionally. \medskip›
lemma bindWT_bindWT:
"bindWT⋅(bindWT⋅m⋅h)⋅k = bindWT⋅m⋅(Λ x. bindWT⋅(h⋅x)⋅k)"
apply (rule writerT_eqI)
apply simp
apply (simp add: bind_bind)
apply (rule cfun_arg_cong)
apply (rule cfun_eqI, simp)
apply (case_tac x)
apply (simp add: bind_strict)
apply (simp add: bind_bind)
apply (rule cfun_arg_cong)
apply (rule cfun_eqI, simp, rename_tac y)
apply (case_tac y)
apply (simp add: bind_strict)
apply (simp add: bind_bind)
apply (rule cfun_arg_cong)
apply (rule cfun_eqI, simp, rename_tac z)
apply (case_tac z)
apply (simp add: bind_strict)
apply (simp add: mappend_assoc)
done
text ‹The right unit monad law is not satisfied in general. \medskip›
lemma bindWT_unitWT_right_counterexample:
fixes m :: "'a⋅('m::monad,'w::monoid) writerT"
assumes "m = WriterT⋅(return⋅⊥)"
assumes "return⋅⊥ ≠ (⊥ :: ('a⋅'w writer)⋅'m)"
shows "bindWT⋅m⋅unitWT ≠ m"
by (simp add: writerT_eq_iff assms)
text ‹Right unit is satisfied for inner monads with a strict ‹return› function. \medskip›
lemma bindWT_unitWT_right_restricted:
fixes m :: "'a⋅('m::monad,'w::monoid) writerT"
assumes "return⋅⊥ = (⊥ :: ('a⋅'w writer)⋅'m)"
shows "bindWT⋅m⋅unitWT = m"
unfolding writerT_eq_iff
apply simp
apply (rule trans [OF _ monad_right_unit])
apply (rule cfun_arg_cong)
apply (rule cfun_eqI)
apply (case_tac x, simp_all add: assms mempty_right)
done
subsection ‹Writer monad transformer invariant›
text ‹We inductively define a predicate that includes all values
that can be constructed from the standard ‹writerT› operations.
\medskip›
inductive invar :: "'a⋅('m::monad, 'w::monoid) writerT ⇒ bool"
where invar_bottom: "invar ⊥"
| invar_lub: "⋀Y. ⟦chain Y; ⋀i. invar (Y i)⟧ ⟹ invar (⨆i. Y i)"
| invar_unitWT: "⋀x. invar (unitWT⋅x)"
| invar_bindWT: "⋀m k. ⟦invar m; ⋀x. invar (k⋅x)⟧ ⟹ invar (bindWT⋅m⋅k)"
| invar_tellWT: "⋀x w. invar (tellWT⋅x⋅w)"
| invar_liftWT: "⋀m. invar (liftWT⋅m)"
text ‹Right unit is satisfied for arguments built from standard
functions. \medskip›
lemma bindWT_unitWT_right_invar:
fixes m :: "'a⋅('m::monad,'w::monoid) writerT"
assumes "invar m"
shows "bindWT⋅m⋅unitWT = m"
using assms proof (induct set: invar)
case invar_bottom thus ?case
by (rule writerT_eqI, simp add: bind_strict)
next
case invar_lub thus ?case
by - (rule admD, simp, assumption, assumption)
next
case invar_unitWT thus ?case
by (rule writerT_eqI, simp add: bind_bind mempty_left)
next
case invar_bindWT thus ?case
apply (simp add: writerT_eq_iff bind_bind)
apply (rule cfun_arg_cong, rule cfun_eqI, simp)
apply (case_tac x, simp add: bind_strict, simp add: bind_bind)
apply (rule cfun_arg_cong, rule cfun_eqI, simp, rename_tac y)
apply (case_tac y, simp add: bind_strict, simp add: mempty_right)
done
next
case invar_tellWT thus ?case
by (simp add: writerT_eq_iff mempty_right)
next
case invar_liftWT thus ?case
by (rule writerT_eqI, simp add: monad_fmap bind_bind mempty_right)
qed
text ‹Left unit is also satisfied for arguments built from standard
functions. \medskip›
lemma writerT_left_unit_invar_lemma:
assumes "invar m"
shows "runWriterT⋅m ⤜ (Λ (Writer⋅w⋅x). return⋅(Writer⋅w⋅x)) = runWriterT⋅m"
using assms proof (induct m set: invar)
case invar_bottom thus ?case
by (simp add: bind_strict)
next
case invar_lub thus ?case
by - (rule admD, simp, assumption, assumption)
next
case invar_unitWT thus ?case
by simp
next
case invar_bindWT thus ?case
apply (simp add: bind_bind)
apply (rule cfun_arg_cong)
apply (rule cfun_eqI, simp, rename_tac n)
apply (case_tac n, simp add: bind_strict)
apply (simp add: bind_bind)
apply (rule cfun_arg_cong)
apply (rule cfun_eqI, simp, rename_tac p)
apply (case_tac p, simp add: bind_strict)
apply simp
done
next
case invar_tellWT thus ?case
by simp
next
case invar_liftWT thus ?case
by (simp add: monad_fmap bind_bind)
qed
lemma bindWT_unitWT_invar:
assumes "invar (k⋅x)"
shows "bindWT⋅(unitWT⋅x)⋅k = k⋅x"
apply (simp add: writerT_eq_iff mempty_left)
apply (rule writerT_left_unit_invar_lemma [OF assms])
done
subsection ‹Invariant expressed as a deflation›
definition invar' :: "'a⋅('m::monad, 'w::monoid) writerT ⇒ bool"
where "invar' m ⟷ fmapWT⋅ID⋅m = m"
text ‹All standard operations preserve the invariant.›
lemma invar'_bottom: "invar' ⊥"
unfolding invar'_def by (simp add: writerT_eq_iff bind_strict)
lemma adm_invar': "adm invar'"
unfolding invar'_def [abs_def] by simp
lemma invar'_unitWT: "invar' (unitWT⋅x)"
unfolding invar'_def by (simp add: writerT_eq_iff)
lemma invar'_bindWT: "⟦invar' m; ⋀x. invar' (k⋅x)⟧ ⟹ invar' (bindWT⋅m⋅k)"
unfolding invar'_def
apply (erule subst)
apply (simp add: writerT_eq_iff)
apply (simp add: bind_bind)
apply (rule cfun_arg_cong)
apply (rule cfun_eqI, case_tac x)
apply (simp add: bind_strict)
apply simp
apply (simp add: bind_bind)
apply (rule cfun_arg_cong)
apply (rule cfun_eqI, rename_tac x, case_tac x)
apply (simp add: bind_strict)
apply simp
done
lemma invar'_tellWT: "invar' (tellWT⋅x⋅w)"
unfolding invar'_def by (simp add: writerT_eq_iff)
lemma invar'_liftWT: "invar' (liftWT⋅m)"
unfolding invar'_def by (simp add: writerT_eq_iff monad_fmap bind_bind)
text ‹Left unit is satisfied for arguments built from fmap.›
lemma bindWT_unitWT_fmapWT:
"bindWT⋅(unitWT⋅x)⋅(Λ x. fmapWT⋅f⋅(k⋅x))
= fmapWT⋅f⋅(k⋅x)"
apply (simp add: fmapWT_def writerT_eq_iff bind_bind)
apply (rule cfun_arg_cong, rule cfun_eqI, simp)
apply (case_tac x, simp_all add: bind_strict mempty_left)
done
text ‹Right unit is satisfied for arguments built from fmap.›
lemma bindWT_fmapWT_unitWT:
shows "bindWT⋅(fmapWT⋅f⋅m)⋅unitWT = fmapWT⋅f⋅m"
apply (simp add: bindWT_fmapWT)
apply (simp add: fmapWT_def)
done
text ‹All monad laws are preserved by values satisfying the invariant.›
lemma invar'_right_unit: "invar' m ⟹ bindWT⋅m⋅unitWT = m"
unfolding invar'_def by (erule subst, rule bindWT_fmapWT_unitWT)
lemma invar'_monad_fmap:
"invar' m ⟹ fmapWT⋅f⋅m = bindWT⋅m⋅(Λ x. unitWT⋅(f⋅x))"
unfolding invar'_def
by (erule subst, simp add: writerT_eq_iff mempty_right)
lemma invar'_bind_assoc:
"⟦invar' m; ⋀x. invar' (f⋅x); ⋀y. invar' (g⋅y)⟧
⟹ bindWT⋅(bindWT⋅m⋅f)⋅g = bindWT⋅m⋅(Λ x. bindWT⋅(f⋅x)⋅g)"
by (rule bindWT_bindWT)
end