Theory Continuations

(*  Title:      Continuations.thy
    Author:     Peter Gammie
*)

section ‹Relating direct and continuation semantics›
(*<*)
theory Continuations
imports
  PCF
begin

(*>*)
text‹

\label{sec:continuations}

This is a fairly literal version of
citet‹"DBLP:conf/icalp/Reynolds74"›, adapted to untyped PCF. A more
abstract account has been given by
citet‹"DBLP:journals/tcs/Filinski07"› in terms of a monadic meta
language, which is difficult to model in Isabelle (but see
citet‹"Huffman:MonadTransformers:2012"›).

We begin by giving PCF a continuation semantics following the modern
account of citet‹"wadler92:_essence_of_funct_progr"›.  We use the
symmetric function space ‹('o ValK, 'o) K → ('o ValK, 'o) K›
as our language includes call-by-name.

›

type_synonym ('a, 'o) K = "('a → 'o) → 'o"

domain 'o ValK
  = ValKF (lazy appKF :: "('o ValK, 'o) K → ('o ValK, 'o) K")
  | ValKTT | ValKFF
  | ValKN (lazy "nat")

type_synonym 'o ValKM = "('o ValK, 'o) K"
(*<*)

lemma ValK_case_ID [simp]:
  "ValK_case⋅ValKF⋅ValKTT⋅ValKFF⋅ValKN = ID"
  apply (rule cfun_eqI)
  apply (case_tac x)
  apply simp_all
  done

lemma below_monic_ValKF [iff]:
  "below_monic_cfun ValKF"
  by (rule below_monicI) simp

lemma below_monic_ValKN [iff]:
  "below_monic_cfun ValKN"
  by (rule below_monicI) simp

(*>*)
text‹

We use the standard continuation monad to ease the semantic
definition.

›

definition unitK :: "'o ValK → 'o ValKM" where
  "unitK ≡ Λ a. Λ c. c⋅a"

definition bindK :: "'o ValKM → ('o ValK → 'o ValKM) → 'o ValKM" where
  "bindK ≡ Λ m k. Λ c. m⋅(Λ a. k⋅a⋅c)"

definition appKM :: "'o ValKM → 'o ValKM → 'o ValKM" where
  "appKM ≡ Λ fK xK. bindK⋅fK⋅(Λ (ValKF⋅f). f⋅xK)"
(*<*)

lemma unitK_simps [simp]:
  "unitK⋅v⋅c = c⋅v"
  unfolding unitK_def by simp

lemma bindK_strict:
  "bindK⋅⊥ = ⊥"
  unfolding bindK_def by (simp add: eta_cfun)

lemma bindK_unitl:
  "bindK⋅(unitK⋅e)⋅f = f⋅e"
  unfolding bindK_def unitK_def by (simp add: eta_cfun)

lemma bindK_unitr:
  "bindK⋅e⋅unitK = e"
  unfolding bindK_def unitK_def by (simp add: eta_cfun)

lemma bindK_assoc:
  "bindK⋅(bindK⋅f⋅g)⋅h = bindK⋅f⋅(Λ v. bindK⋅(g⋅v)⋅h)"
  unfolding bindK_def unitK_def by simp

lemmas bindK_simps[simp] = bindK_strict bindK_unitl bindK_unitr bindK_assoc

(*>*)
text‹The interpretations of the constants.›

definition
  condK :: "'o ValKM → 'o ValKM → 'o ValKM → 'o ValKM"
where
  "condK ≡ Λ iK tK eK. bindK⋅iK⋅(Λ i. case i of
                ValKF⋅f ⇒ ⊥ | ValKTT ⇒ tK | ValKFF ⇒ eK | ValKN⋅n ⇒ ⊥)"

definition succK :: "'o ValKM → 'o ValKM" where
  "succK ≡ Λ nK. bindK⋅nK⋅(Λ (ValKN⋅n). unitK⋅(ValKN⋅(n + 1)))"

definition predK :: "'o ValKM → 'o ValKM" where
  "predK ≡ Λ nK. bindK⋅nK⋅(Λ (ValKN⋅n). case n of 0 ⇒ ⊥ | Suc n ⇒ unitK⋅(ValKN⋅n))"

definition isZeroK :: "'o ValKM → 'o ValKM" where
  "isZeroK ≡ Λ nK. bindK⋅nK⋅(Λ (ValKN⋅n). unitK⋅(if n = 0 then ValKTT else ValKFF))"

text ‹

A continuation semantics for PCF. If we had defined our direct
semantics using a monad then the correspondence would be more
syntactically obvious.

›

type_synonym 'o EnvK = "'o ValKM Env"

primrec
  evalK :: "expr ⇒ 'o EnvK → 'o ValKM"
where
  "evalK (Var v) = (Λ ρ. ρ⋅v)"
| "evalK (App f x) = (Λ ρ. appKM⋅(evalK f⋅ρ)⋅(evalK x⋅ρ))"
| "evalK (AbsN v e) = (Λ ρ. unitK⋅(ValKF⋅(Λ x. evalK e⋅(env_ext⋅v⋅x⋅ρ))))"
| "evalK (AbsV v e) = (Λ ρ. unitK⋅(ValKF⋅(Λ x c. x⋅(Λ x'. evalK e⋅(env_ext⋅v⋅(unitK⋅x')⋅ρ)⋅c))))"
| "evalK (Diverge) = (Λ ρ. ⊥)"
| "evalK (Fix v e) = (Λ ρ. μ x. evalK e⋅(env_ext⋅v⋅x⋅ρ))"
| "evalK (tt) = (Λ ρ. unitK⋅ValKTT)"
| "evalK (ff) = (Λ ρ. unitK⋅ValKFF)"
| "evalK (Cond i t e) = (Λ ρ. condK⋅(evalK i⋅ρ)⋅(evalK t⋅ρ)⋅(evalK e⋅ρ))"
| "evalK (Num n) = (Λ ρ. unitK⋅(ValKN⋅n))"
| "evalK (Succ e) = (Λ ρ. succK⋅(evalK e⋅ρ))"
| "evalK (Pred e) = (Λ ρ. predK⋅(evalK e⋅ρ))"
| "evalK (IsZero e) = (Λ ρ. isZeroK⋅(evalK e⋅ρ))"

text‹

To establish the chain completeness (admissibility) of our logical
relation, we need to show that @{term "unitK"} is an \emph{order
monic}, i.e., if @{term "unitK⋅x ⊑ unitK⋅y"} then @{term "x ⊑
y"}. This is an order-theoretic version of injectivity.

In order to define a continuation that witnesses this, we need to be
able to distinguish converging and diverging computations. We
therefore require our observation domain to contain at least two
elements:

›

locale at_least_two_elements =
  fixes some_non_bottom_element :: "'o::domain"
  assumes some_non_bottom_element: "some_non_bottom_element ≠ ⊥"

text‹

Following citet‹"DBLP:conf/icalp/Reynolds74"› and citet‹‹Remark
47› in "DBLP:journals/tcs/Filinski07"› we use the following continuation:

›

lemma cont_below [simp, cont2cont]:
  "cont (λx::'a::pcpo. if x ⊑ d then ⊥ else c)"
(*<*)
proof(rule contI2)
  show "monofun (λx. if x ⊑ d then ⊥ else c)"
    apply (rule monofunI)
    apply clarsimp
    apply (cut_tac x=x and y=y and z=d in below_trans)
    apply auto
    done
next
  fix Y :: "nat ⇒ 'a::pcpo"
  assume Y: "chain Y"
  assume "chain (λi. if Y i ⊑ d then ⊥ else c)"
  show "(if (⨆ i. Y i) ⊑ d then ⊥ else c) ⊑ (⨆ i. if Y i ⊑ d then ⊥ else c)"
  proof(cases "∀i. Y i ⊑ d")
    case True hence "Lub Y ⊑ d"
      using lub_below_iff[OF Y] by simp
    thus ?thesis by simp
  next
    case False
    let ?f = "λi. if Y i ⊑ d then ⊥ else c"
    from False have LubY: "¬ Lub Y ⊑ d"
      using lub_below_iff[OF Y] by simp
    from False have F: "chain ?f"
      apply -
      apply (rule chainI)
      apply clarsimp
      apply (cut_tac i=i and j="Suc i" in chain_mono[OF Y])
      apply clarsimp
      apply (cut_tac x="Y i" and y="Y (Suc i)" and z=d in below_trans)
      apply simp_all
      done
    from False obtain i where Yi: "¬ Y i ⊑ d" by blast
    hence M: "max_in_chain i ?f"
      apply -
      apply (rule max_in_chainI)
      apply clarsimp
      apply (drule chain_mono[OF Y])
      apply (cut_tac x="Y i" and y="Y j" and z=d in below_trans)
      apply simp_all
      done
    from LubY Yi show ?thesis
      using iffD1[OF maxinch_is_thelub, OF F M] by simp
  qed
qed
(*>*)
text‹›

lemma (in at_least_two_elements) below_monic_unitK [intro, simp]:
  "below_monic_cfun (unitK :: 'o ValK → 'o ValKM)"
proof(rule below_monicI)
  fix v v' :: "'o ValK"
  assume vv': "unitK⋅v ⊑ unitK⋅v'"
  let ?k = "Λ x. if x ⊑ v' then ⊥ else some_non_bottom_element"
  from vv' have "unitK⋅v⋅?k ⊑ unitK⋅v'⋅?k" by (rule monofun_cfun_fun)
  hence "?k⋅v ⊑ ?k⋅v'" by (simp add: unitK_def)
  with some_non_bottom_element show "v ⊑ v'" by (auto split: if_split_asm)
qed


subsection‹Logical relation›

text‹

We follow citet‹"DBLP:conf/icalp/Reynolds74"› by simultaneously
defining a pair of relations over values and functions. Both are
bottom-reflecting, in contrast to the situation for computational
adequacy in \S\ref{sec:compad}.  citet‹"DBLP:journals/tcs/Filinski07"›
differs by assuming that values are always defined, and relates values
and monadic computations.

›

type_synonym 'o lfr = "(ValD, 'o ValKM, ValD → ValD, 'o ValKM → 'o ValKM) lf_pair_rep"
type_synonym 'o lflf = "(ValD, 'o ValKM, ValD → ValD, 'o ValKM → 'o ValKM) lf_pair"

context at_least_two_elements
begin

abbreviation lr_eta_rep_N where
  "lr_eta_rep_N ≡ { (e, e') .
       (e = ⊥ ∧ e' = ⊥)
     ∨ (e = ValTT ∧ e' = unitK⋅ValKTT)
     ∨ (e = ValFF ∧ e' = unitK⋅ValKFF)
     ∨ (∃n. e = ValN⋅n ∧ e' = unitK⋅(ValKN⋅n)) }"

abbreviation lr_eta_rep_F where
  "lr_eta_rep_F ≡ λ(rm, rp). { (e, e') .
       (e = ⊥ ∧ e' = ⊥)
     ∨ (∃f f'. e = ValF⋅f ∧ e' = unitK⋅(ValKF⋅f') ∧ (f, f') ∈ unlr (snd rp)) }"

definition lr_eta_rep where
  "lr_eta_rep ≡ λr. lr_eta_rep_N ∪ lr_eta_rep_F r"

definition lr_theta_rep where
  "lr_theta_rep ≡ λ(rm, rp). { (f, f') .
     (∀(x, x') ∈ unlr (fst (undual rm)). (f⋅x, f'⋅x') ∈ unlr (fst rp)) }"

definition lr_rep :: "'o lfr" where
  "lr_rep ≡ λr. (lr_eta_rep r, lr_theta_rep r)"

abbreviation lr :: "'o lflf" where
  "lr ≡ λr. (mklr (fst (lr_rep r)), mklr (snd (lr_rep r)))"
(*<*)
text‹

Properties of the logical relation.

›

lemma admS_eta_rep [intro, simp]:
  "fst (lr_rep r) ∈ admS"
  unfolding lr_rep_def lr_eta_rep_def
  apply simp
  apply rule
  apply (auto intro!: adm_disj simp: split_def)
   using adm_below_monic_exists[OF _ below_monic_pair[OF below_monic_ValN below_monic_cfcomp2[OF below_monic_unitK below_monic_ValKN]], where P="λ_. True"]
   apply (simp add: prod_eq_iff)
  using adm_below_monic_exists[OF _ below_monic_pair_split[OF below_monic_ValF below_monic_cfcomp2[OF below_monic_unitK below_monic_ValKF]], where P="λx. x ∈ unlr (snd (snd r))"]
  apply (simp add: prod_eq_iff)
  done

lemma admS_theta_rep [intro, simp]:
  "snd (lr_rep r) ∈ admS"
proof
  show "⊥ ∈ snd (lr_rep r)"
    unfolding lr_rep_def lr_theta_rep_def
    by (cases r) simp
next
  show "adm (λx. x ∈ snd (lr_rep r))"
    apply (rule admI)
    unfolding lr_rep_def lr_theta_rep_def
    apply (clarsimp simp: split_def)
    apply (rule admD)
      apply (rule adm_subst)
      apply force+
    done
qed

lemma mono_lr:
  shows "mono (lr :: 'o lflf)"
  apply (rule monoI)
  apply simp
  apply (simp add: lr_rep_def)
  apply (rule conjI)
   apply (force simp: lr_eta_rep_def split_def dual_less_eq_iff unlr_leq[symmetric])
  (* FIXME stuck with the projections on the product *)
  apply (auto simp: lr_theta_rep_def split_def dual_less_eq_iff unlr_leq[symmetric])
  apply (drule_tac x="(ae, bc)" in bspec)
   apply (case_tac a)
   apply (case_tac ab)
   apply (auto simp: unlr_leq[symmetric])
  done

(*>*)
end (* context at_least_two_elements *)

text‹

It takes some effort to set up the minimal invariant relating the two
pairs of domains. One might hope this would be easier using deflations
(which might compose) rather than ``copy'' functions (which certainly
don't).

We elide these as they are tedious.

›
(*<*)

primrec
  ValD_copy_i :: "nat ⇒ ValD → ValD"
where
  "ValD_copy_i 0 = ⊥"
| "ValD_copy_i (Suc n) = (Λ v. case v of ValF⋅f ⇒ ValF⋅(ValD_copy_i n oo f oo ValD_copy_i n) | ValTT ⇒ ValTT | ValFF ⇒ ValFF | ValN⋅m ⇒ ValN⋅m)"

abbreviation
  "ValD_copy_lub ≡ (⨆i. ValD_copy_i i)"

lemma ValD_copy_chain [intro, simp]:
  "chain ValD_copy_i"
proof(rule chainI)
  fix i show "ValD_copy_i i ⊑ ValD_copy_i (Suc i)"
  proof(induct i)
    case (Suc i)
    { fix x
      have "ValD_copy_i (Suc i)⋅x ⊑ ValD_copy_i (Suc (Suc i))⋅x"
      proof(cases x)
        case (ValF f) with Suc show ?thesis
          by (clarsimp simp: cfcomp1 cfun_below_iff monofun_cfun)
      qed simp_all }
    thus ?case by (simp add: cfun_below_iff)
  qed simp
qed

lemma ValD_copy_lub_ID:
  "ValD_copy_lub = ID"
proof -
  { fix x :: ValD
    fix i :: nat
    have "ValD_take i⋅(ValD_copy_i i⋅(ValD_take i⋅x)) = ValD_take i⋅x"
    proof (induct i arbitrary: x)
      case (Suc n) thus ?case
        by (cases x) (simp_all add: cfun_map_def)
    qed simp }
  hence "⋀x :: ValD. (⨆i. ValD_take i⋅(ValD_copy_i i⋅(ValD_take i⋅x))) = (⨆i. ValD_take i⋅x)"
    by (blast intro: lub_eq)
  thus ?thesis by (simp add: lub_distribs ValD.lub_take cfun_eq_iff)
qed

text‹

Continuations: we need to ensure the observation type is always the
same.

›

definition
  KM_map :: "('o ValK → 'o ValK) → 'o ValKM → 'o ValKM"
where
  "KM_map ≡ Λ f. cfun_map⋅(cfun_map⋅f⋅ID)⋅ID"

lemma KM_map_id [intro, simp]:
  "KM_map⋅ID = ID"
  unfolding KM_map_def by (simp add: cfun_map_ID)

lemma KM_map_strict [intro, simp]:
  "KM_map⋅f⋅⊥ = ⊥"
  unfolding KM_map_def by (simp add: cfun_map_def)

primrec
  ValK_copy_i :: "nat ⇒ 'o ValK → 'o ValK"
where
  "ValK_copy_i 0 = ⊥"
| "ValK_copy_i (Suc n) = (Λ v. case v of ValKF⋅f ⇒ ValKF⋅(cfun_map⋅(KM_map⋅(ValK_copy_i n))⋅(KM_map⋅(ValK_copy_i n))⋅f) | ValKTT ⇒ ValKTT | ValKFF ⇒ ValKFF | ValKN⋅m ⇒ ValKN⋅m)"

abbreviation
  "ValK_copy ≡ (⨆i. ValK_copy_i i)"

lemma ValK_copy_chain [intro, simp]:
  "chain (ValK_copy_i :: nat ⇒ 'o ValK → 'o ValK)"
proof(rule chainI)
  fix i show "ValK_copy_i i ⊑ (ValK_copy_i (Suc i) :: 'o ValK → 'o ValK)"
  proof(induct i)
    case (Suc i)
    { fix x :: "'o ValK"
      have "ValK_copy_i (Suc i)⋅x ⊑ ValK_copy_i (Suc (Suc i))⋅x"
      proof(cases x)
        case (ValKF f) with Suc show ?thesis by (clarsimp simp: monofun_cfun KM_map_def)
      qed simp_all }
    thus ?case by (simp add: cfun_below_iff)
  qed simp
qed

lemma ValK_copy_fix:
  "ValK_copy = (ID :: 'o ValK → 'o ValK)"
proof -
  { fix x :: "'o ValK"
    fix i :: nat
    have "ValK_take i⋅(ValK_copy_i i⋅(ValK_take i⋅x)) = ValK_take i⋅x"
    proof (induct i arbitrary: x)
      case (Suc n) thus ?case
        by (cases x) (simp_all add: KM_map_def cfun_map_def)
    qed simp }
  hence "⋀x :: 'o ValK. (⨆i. ValK_take i⋅(ValK_copy_i i⋅(ValK_take i⋅x))) = (⨆i. ValK_take i⋅x)"
    by (blast intro: lub_eq)
  thus ?thesis by (simp add: lub_distribs ValK.lub_take cfun_eq_iff)
qed

lemma ValK_strict [intro, simp]:
  "ValK_copy⋅⊥ = ⊥"
  by (simp add: ValK_copy_fix)

text‹

We need to respect the purported domain structure, and positive and
negative occurrences.

›

fixrec
  ValD_copy_rec :: "((ValD → ValD) × ((ValD → ValD) → (ValD → ValD)))
                  → ((ValD → ValD) × ((ValD → ValD) → (ValD → ValD)))"
where
  "ValD_copy_rec⋅r =
     (Λ v. case v of ValF⋅f ⇒ ValF⋅(snd r⋅f) | ValTT ⇒ ValTT | ValFF ⇒ ValFF | ValN⋅n ⇒ ValN⋅n,
      Λ f. cfun_map⋅(strictify⋅(fst r))⋅(strictify⋅(fst r))⋅f)"

abbreviation
  ValD_copy_eta :: "nat ⇒ ValD → ValD"
where
  "ValD_copy_eta i ≡ fst (iterate i⋅ValD_copy_rec⋅⊥)"

abbreviation
  ValD_copy_theta :: "nat ⇒ (ValD → ValD) → (ValD → ValD)"
where
  "ValD_copy_theta i ≡ snd (iterate i⋅ValD_copy_rec⋅⊥)"

lemma ValD_copy_eta_theta_strict [intro, simp]:
  "ValD_copy_eta n⋅⊥ = ⊥"
  "ValD_copy_theta n⋅⊥ = ⊥"
  by (induct n) (simp_all add: cfun_map_def)

lemma ValD_copy_fix_strict [simp]:
  "fst (fix⋅ValD_copy_rec)⋅⊥ = ⊥"
  "snd (fix⋅ValD_copy_rec)⋅⊥ = ⊥"
  by (subst fix_eq, simp add: cfun_map_def)+

lemma ValD_copy_rec_ValD_copy_lub:
  "fix⋅ValD_copy_rec = (ValD_copy_lub, cfun_map⋅ValD_copy_lub⋅ValD_copy_lub)" (is "?lhs = ?rhs")
proof(rule below_antisym)
  show "?lhs ⊑ ?rhs"
    apply (rule fix_least)
    apply (simp add: eta_cfun strictify_cancel ValD_copy_lub_ID cfun_map_def ID_def)
    done
next
  { fix i have "ValD_copy_i i ⊑ fst (fix⋅ValD_copy_rec)"
    proof(induct i)
      case (Suc i) thus ?case
        apply -
        apply (subst fix_eq)
        apply (subst fix_eq)
        apply (simp add: eta_cfun strictify_cancel cfcomp1 cfun_map_def)
        apply (intro monofun_cfun_fun monofun_cfun_arg)
        apply (simp add: eta_cfun strictify_cancel monofun_cfun cfcomp1 cfun_map_def cfun_below_iff)
        done
    qed simp }
  hence D: "ValD_copy_lub ⊑ fst (fix⋅ValD_copy_rec)" by (simp add: lub_below_iff)
  moreover
  from D
  have "cfun_map⋅ValD_copy_lub⋅ValD_copy_lub ⊑ snd (fix⋅ValD_copy_rec)"
    by (subst fix_eq) (simp add: eta_cfun strictify_cancel monofun_cfun)
  ultimately show "?rhs ⊑ ?lhs" by (simp add: prod_belowI)
qed

lemma fix_ValD_copy_rec_ID:
  "fix⋅ValD_copy_rec = (ID, ID)"
  using ValD_copy_rec_ValD_copy_lub ValD_copy_lub_ID cfun_map_ID
  by simp

fixrec
  ValK_copy_rec :: "(('o ValKM → 'o ValKM) × (('o ValKM → 'o ValKM) → ('o ValKM → 'o ValKM)))
                  → ('o ValKM → 'o ValKM) × (('o ValKM → 'o ValKM) → ('o ValKM → 'o ValKM))"
where
  "ValK_copy_rec⋅r =
     (Λ vm. KM_map⋅(Λ v. case v of ValKF⋅f ⇒ ValKF⋅(snd r⋅f) | ValKTT ⇒ ValKTT | ValKFF ⇒ ValKFF | ValKN⋅a ⇒ ValKN⋅a)⋅vm,
      Λ f. cfun_map⋅(strictify⋅(fst r))⋅(strictify⋅(fst r))⋅f)"

abbreviation
  ValK_copy_eta
where
  "ValK_copy_eta i ≡ fst (iterate i⋅ValK_copy_rec⋅⊥)"

abbreviation
  ValK_copy_theta
where
  "ValK_copy_theta i ≡ snd (iterate i⋅ValK_copy_rec⋅⊥)"

lemma ValK_copy_strict [intro, simp]:
  "ValK_copy_eta n⋅⊥ = (⊥ :: 'o ValKM)"
  "ValK_copy_theta n⋅⊥ = (⊥ :: 'o ValKM → 'o ValKM)"
  by (induct n) (simp_all add: cfun_map_def)

lemma ValK_copy_fix_strict [simp]:
  "fst (fix⋅ValK_copy_rec)⋅⊥ = ⊥"
  "snd (fix⋅ValK_copy_rec)⋅⊥ = ⊥"
  by (subst fix_eq, simp add: cfun_map_def)+

lemma ValK_copy_rec_ValK_copy:
  "fix⋅ValK_copy_rec
  = (KM_map⋅(ValK_copy :: 'o ValK → 'o ValK),
     cfun_map⋅(KM_map⋅ValK_copy)⋅(KM_map⋅ValK_copy))" (is "?lhs = ?rhs")
proof(rule below_antisym)
  show "?lhs ⊑ ?rhs"
    apply (rule fix_least)
    apply (simp add: eta_cfun strictify_cancel cfun_map_def ID_def)
    apply (intro cfun_eqI)
    apply (simp add: KM_map_def cfun_map_def ValK_copy_fix eta_cfun)
    done
next
  { fix i
    have "KM_map⋅(ValK_copy_i i :: 'o ValK → 'o ValK) ⊑ fst (fix⋅ValK_copy_rec)"
     and "(ValK_copy_i i :: 'o ValK → 'o ValK) ⊑ ValK_case⋅(Λ f. ValKF⋅(snd (fix⋅ValK_copy_rec)⋅f))⋅ValKTT⋅ValKFF⋅ValKN"
    proof(induct i)
      case 0
      { case 1 show ?case
          apply (subst fix_eq)
          apply (auto iff: cfun_below_iff intro!: monofun_cfun_arg simp: KM_map_def cfun_map_def eta_cfun)
          done }
      { case 2 show ?case by simp }
    next
      case (Suc i)
      { case 1 from Suc show ?case
          apply -
          apply (subst fix_eq)
          apply (subst fix_eq)
          apply (simp add: eta_cfun strictify_cancel cfcomp1 cfun_map_def KM_map_def)
          apply (intro cfun_belowI)
          apply (simp add: eta_cfun strictify_cancel cfcomp1 cfun_map_def KM_map_def)
          apply (intro monofun_cfun_arg)
          apply (intro cfun_belowI)
          apply (simp add: eta_cfun strictify_cancel cfcomp1 cfun_map_def KM_map_def)
          apply (intro monofun_cfun_arg)
          apply (simp add: eta_cfun strictify_cancel monofun_cfun cfcomp1 cfun_map_def)
          apply (intro monofun_cfun)
          apply simp_all
          apply (intro cfun_belowI)
          apply (simp add: eta_cfun strictify_cancel monofun_cfun cfcomp1 cfun_map_def)
          apply (intro cfun_belowI)
          apply (subst fix_eq)
          apply (simp add: eta_cfun strictify_cancel monofun_cfun cfcomp1 KM_map_def cfun_map_def)
          apply (intro monofun_cfun)
          apply simp_all
          apply (subst fix_eq)
          apply (intro cfun_belowI)
          apply (simp add: eta_cfun strictify_cancel monofun_cfun cfcomp1 KM_map_def cfun_map_def)
          apply (intro monofun_cfun)
          apply simp_all
          apply (intro cfun_belowI)
          apply simp
          apply (intro monofun_cfun)
          apply simp_all
          apply (intro cfun_belowI)
          apply simp
          apply (intro monofun_cfun)
          apply simp_all
          done }
      { case 2 from Suc show ?case
          apply -
          apply (subst fix_eq)
          apply (subst fix_eq)
          apply (auto simp: eta_cfun strictify_cancel cfcomp1 cfun_map_def KM_map_def cfun_below_iff intro!: monofun_cfun)
          done }
    qed }
  hence "(⨆i. KM_map⋅(ValK_copy_i i :: 'o ValK → 'o ValK)) ⊑ fst (fix⋅ValK_copy_rec)"
    by (simp add: lub_below_iff)
  hence D: "KM_map⋅(ValK_copy :: 'o ValK → 'o ValK) ⊑ fst (fix⋅ValK_copy_rec)"
    by (simp add: contlub_cfun_arg[symmetric])
  from D
  have E: "cfun_map⋅(KM_map⋅(ValK_copy :: 'o ValK → 'o ValK))⋅(KM_map⋅ValK_copy) ⊑ snd (fix⋅ValK_copy_rec)"
    apply (subst fix_eq)
    apply (simp add: eta_cfun strictify_cancel KM_map_def)
    apply (intro monofun_cfun)
    apply simp_all
    done
  show "?rhs ⊑ ?lhs" by (simp add: prod_belowI D E)
qed

lemma fix_ValK_copy_rec_ID:
  "fix⋅ValK_copy_rec = (ID, ID)"
  by (simp add: ValK_copy_rec_ValK_copy ValK_copy_fix cfun_map_ID)

lemma (in at_least_two_elements) min_inv_lr:
  assumes "fst ea⋅⊥ = ⊥"
  assumes "fst eb⋅⊥ = ⊥"
  assumes "eRSP ea eb R S"
  shows "eRSP (ValD_copy_rec⋅ea) (ValK_copy_rec⋅eb) (dual ((lr :: 'o lflf) (dual S, undual R))) (lr (R, S))"
  using assms some_non_bottom_element
  apply (simp add: split_def)
  apply (auto simp: split_def lr_rep_def lr_eta_rep_def lr_theta_rep_def KM_map_def cfun_map_def unitK_def)
  apply (force simp: cfun_map_def strictify_cancel unitK_def) (* FIXME obvious but auto misses it *)
  done

(*>*)
sublocale at_least_two_elements < F: DomSolP lr ValD_copy_rec ValK_copy_rec
  apply standard
         apply (rule mono_lr)
        apply (rule fix_ValD_copy_rec_ID)
       apply (rule fix_ValK_copy_rec_ID)
      apply (simp_all add: cfun_map_def)[4]
  apply (erule (2) min_inv_lr)
  done


subsection‹A retraction between the two definitions›

text‹

We can use the relation to establish a strong connection between the
direct and continuation semantics.  All results depend on the
observation type being rich enough.

›

context at_least_two_elements
begin

abbreviation mrel (‹η: _ ↦ _› [50, 51] 50) where
  "η: x ↦ x' ≡ (x, x') ∈ unlr (fst F.delta)"

abbreviation vrel (‹θ: _ ↦ _› [50, 51] 50) where
  "θ: y ↦ y' ≡ (y, y') ∈ unlr (snd F.delta)"
(*<*)
lemma F_bottom_triv [intro, simp]:
  "η: ⊥ ↦ ⊥"
  "θ: ⊥ ↦ ⊥"
  by simp_all

lemma etaI [intro, simp]:
  "η: ValTT ↦ unitK⋅ValKTT"
  "η: ValFF ↦ unitK⋅ValKFF"
  "η: ValN⋅n ↦ unitK⋅(ValKN⋅n)"
  by (subst F.delta_sol, simp, simp add: lr_rep_def lr_eta_rep_def)+

lemma eta_F:
  "θ: f ↦ f' ⟹ η: ValF⋅f ↦ unitK⋅(ValKF⋅f')"
apply (subst F.delta_sol)
apply simp
apply (subst lr_rep_def)
apply (fastforce simp: lr_eta_rep_def split_def)
done

lemma theta_F:
  "(⋀x x'. η: x ↦ x' ⟹ η: f⋅x ↦ f'⋅x') ⟹ θ: f ↦ f'"
apply (subst F.delta_sol)
apply simp
apply (subst lr_rep_def)
apply (simp add: lr_theta_rep_def split_def)
done

lemma eta_induct[case_names bottom N F, consumes 1]:
  "⟦ η: x ↦ x';
     ⟦ x = ⊥; x' = ⊥ ⟧ ⟹ P x x';
     ⋀n. ⟦ x = ValTT; x' = unitK⋅ValKTT ⟧ ⟹ P x x';
     ⋀n. ⟦ x = ValFF; x' = unitK⋅ValKFF ⟧ ⟹ P x x';
     ⋀n. ⟦ x = ValN⋅n; x' = unitK⋅(ValKN⋅n) ⟧ ⟹ P x x';
     ⋀f f'. ⟦ x = ValF⋅f; x' = unitK⋅(ValKF⋅f'); θ: f ↦ f' ⟧ ⟹ P x x'
   ⟧ ⟹ P x x'"
  apply (cases x)
      apply (subst (asm) F.delta_sol, simp, simp add: lr_rep_def lr_eta_rep_def)
     defer
     apply (subst (asm) F.delta_sol, simp, simp add: lr_rep_def lr_eta_rep_def)
    apply (subst (asm) F.delta_sol, simp, simp add: lr_rep_def lr_eta_rep_def)
   apply (subst (asm) F.delta_sol, simp, simp add: lr_rep_def lr_eta_rep_def)
  apply simp
  apply (subst (asm) F.delta_sol)
  apply simp
  apply (subst (asm) lr_rep_def)
  apply (subst (asm) lr_eta_rep_def)
  apply clarsimp
  done

lemma theta_induct[case_names F, consumes 1]:
  "⟦ θ: f ↦ f';
     (⋀x x'. η: x ↦ x' ⟹ η: f⋅x ↦ f'⋅x') ⟹ P f f'
   ⟧ ⟹ P f f'"
  apply (subst (asm) F.delta_sol)
  apply simp
  apply (subst (asm) lr_rep_def)
  apply (subst (asm) lr_theta_rep_def)
  apply fastforce
  done

(*>*)
text‹

Theorem 1 from citet‹"DBLP:conf/icalp/Reynolds74"›.

›

lemma AbsV_aux:
  assumes "η: ValF⋅f ↦ unitK⋅(ValKF⋅f')"
  shows "η: ValF⋅(strictify⋅f) ↦ unitK⋅(ValKF⋅(Λ x c. x⋅(Λ x'. f'⋅(unitK⋅x')⋅c)))"
(*<*)
  apply (rule eta_F)
  apply (rule theta_F)
  using assms
  apply (rule eta_induct) (* FIXME special rule would help *)
  apply simp_all
  apply (drule injD[OF below_monic_inj[OF below_monic_unitK]])
  apply clarsimp
  apply (erule theta_induct)
  apply (erule eta_induct)
  apply (simp_all add: eta_cfun eta_F)
  done

(*>*)
text‹›

theorem Theorem1:
  assumes "∀v. η: ρ⋅v ↦ ρ'⋅v"
  shows "η: evalD e⋅ρ ↦ evalK e⋅ρ'"
(*<*)
using assms
proof(induct e arbitrary: ρ ρ')
  case App show ?case
    apply simp
    apply (simp add: appKM_def bindK_def)
    using App.hyps(1)[OF App(3)]
    apply (rule eta_induct)
      apply simp_all
    apply (simp add: eta_cfun)
    apply (erule theta_induct)
    using App.hyps(2)[OF App(3)]
    apply simp
    done
next
  case AbsN show ?case
    apply simp
    apply (rule eta_F)
    apply (rule theta_F)
    apply simp
    apply (rule AbsN.hyps)
    using AbsN(2)
    unfolding env_ext_def
    apply simp
    done
next
  case (AbsV v e ρ ρ')
  have "η: ValF⋅(Λ x. evalD e⋅(env_ext⋅v⋅x⋅ρ)) ↦ unitK⋅(ValKF⋅(Λ x. evalK e⋅(env_ext⋅v⋅x⋅ρ')))"
    apply (rule eta_F)
    apply (rule theta_F)
    apply simp
    apply (rule AbsV.hyps)
    using AbsV(2)
    unfolding env_ext_def
    apply simp
    done
  thus ?case by (fastforce dest: AbsV_aux)
next
  case Fix thus ?case
    apply simp
    apply (rule parallel_fix_ind)
    apply (simp_all add: env_ext_def)
    done
next
  case Cond thus ?case
    apply (simp add: cond_def condK_def eta_cfun)
    using Cond(1)[OF Cond(4)]
    apply (rule eta_induct)
    apply simp_all
    done
next
  case Succ thus ?case
    apply (simp add: succ_def succK_def eta_cfun)
    using Succ(1)[OF Succ(2)]
    apply (rule eta_induct)
    apply simp_all
    done
next
  case Pred thus ?case
    apply (simp add: pred_def predK_def eta_cfun)
    using Pred(1)[OF Pred(2)]
    apply (rule eta_induct)
    apply (simp_all split: nat.split)
    done
next
  case IsZero thus ?case
    apply (simp add: isZero_def isZeroK_def eta_cfun)
    using IsZero(1)[OF IsZero(2)]
    apply (rule eta_induct)
    apply (simp_all split: nat.split)
    done
qed simp_all
(*>*)

end


text‹

The retraction between the two value and monadic value spaces.

Note we need to work with an observation type that can represent the
``explicit values'', i.e. @{typ "'o ValK"}.

›

locale value_retraction =
  fixes VtoO :: "'o ValK → 'o"
  fixes OtoV :: "'o → 'o ValK"
  assumes OV: "OtoV oo VtoO = ID"

sublocale value_retraction < at_least_two_elements "VtoO⋅(ValKN⋅0)"
using OV by - (standard, simp add: injection_defined cfcomp1 cfun_eq_iff)

context value_retraction
begin

fun
  DtoKM_i :: "nat ⇒ ValD → 'o ValKM"
and
  KMtoD_i :: "nat ⇒ 'o ValKM → ValD"
where
  "DtoKM_i 0 = ⊥"
| "DtoKM_i (Suc n) = (Λ v. case v of
     ValF⋅f ⇒ unitK⋅(ValKF⋅(cfun_map⋅(KMtoD_i n)⋅(DtoKM_i n)⋅f))
   | ValTT ⇒ unitK⋅ValKTT
   | ValFF ⇒ unitK⋅ValKFF
   | ValN⋅m ⇒ unitK⋅(ValKN⋅m))"

| "KMtoD_i 0 = ⊥"
| "KMtoD_i (Suc n) = (Λ v. case OtoV⋅(v⋅VtoO) of
     ValKF⋅f ⇒ ValF⋅(cfun_map⋅(DtoKM_i n)⋅(KMtoD_i n)⋅f)
   | ValKTT ⇒ ValTT
   | ValKFF ⇒ ValFF
   | ValKN⋅m ⇒ ValN⋅m)"

abbreviation "DtoKM ≡ (⨆i. DtoKM_i i)"
abbreviation "KMtoD ≡ (⨆i. KMtoD_i i)"
(*<*)

lemma DtoKM_KMtoD_chain [intro, simp]:
  "chain DtoKM_i"
  "chain KMtoD_i"
proof -
  let ?C = "λi. (DtoKM_i i, KMtoD_i i)"
  have "chain ?C"
  proof(rule chainI)
    fix i show "?C i ⊑ ?C (Suc i)"
    proof(induct i)
      case (Suc i) show ?case
      proof(rule monofun_pair)
        show "DtoKM_i (Suc i) ⊑ DtoKM_i (Suc (Suc i))"
        proof(rule cfun_belowI)
          fix x from Suc show "DtoKM_i (Suc i)⋅x ⊑ DtoKM_i (Suc (Suc i))⋅x"
            by (cases x) (auto intro!: monofun_cfun simp: cfun_map_def cfun_below_iff)
        qed
        show "KMtoD_i (Suc i) ⊑ KMtoD_i (Suc (Suc i))"
        proof(rule cfun_belowI)
          fix x from Suc show "KMtoD_i (Suc i)⋅x ⊑ KMtoD_i (Suc (Suc i))⋅x"
            apply (simp add: eta_cfun)
            apply (intro monofun_cfun_fun monofun_cfun_arg)
            apply (intro cfun_belowI)
            apply (auto intro: monofun_cfun)
            done
        qed
      qed
    qed simp
  qed
  then show "chain DtoKM_i" "chain KMtoD_i"
    by (auto dest: ch2ch_fst ch2ch_snd)
qed
(*>*)

text‹

Lemma 1 from citet‹"DBLP:conf/icalp/Reynolds74"›.

›

lemma Lemma1:
  "η: x ↦ DtoKM⋅x"
  "η: x ↦ x' ⟹ x = KMtoD⋅x'"
(*<*)
proof -
  have K: "η: ValD_copy_i i⋅x ↦ DtoKM_i i⋅x"
   and L: "η: x ↦ x' ⟹ ValD_copy_i i⋅x = KMtoD_i i⋅x'" for x x' i
  proof(induct i arbitrary: x x')
    case (Suc i)
    { case 1 show ?case
        apply (cases x)
          apply simp_all
        apply (rule eta_F)
        apply (rule theta_F)
        using Suc
        apply simp
        done }
    { case 2 thus ?case
        apply (induct rule: eta_induct)
          using OV
          apply (simp_all add: cfun_eq_iff retraction_strict)
        apply (clarsimp simp: cfun_eq_iff)
        apply (erule theta_induct)
        using Suc
        apply simp
        done }
  qed simp_all
  let ?C1 = "λi. (ValD_copy_i i, DtoKM_i i)"
  let ?P1 = "λf. η: (fst f)⋅x ↦ (snd f)⋅x"
  have "adm ?P1" by (rule adm_subst) simp_all
  with K
  have "?P1 (⨆i. ValD_copy_i i, ⨆i. DtoKM_i i)"
    using admD[where P="?P1" and Y="?C1"] lub_prod[where S="?C1"] by simp
  moreover
  { fix x :: ValD
    fix x' :: "'o ValKM"
    let ?C2 = "λi. (ValD_copy_i i, KMtoD_i i)"
    let ?P2 = "λf. (fst f)⋅x = (snd f)⋅x'"
    have "adm (λf. ?P2 f)" by simp
    with L have "η: x ↦ x' ⟹ ?P2 (⨆i. ValD_copy_i i, ⨆i. KMtoD_i i)"
      using admD[where P="?P2" and Y="?C2"] lub_prod[where S="?C2"]
      by simp }
  ultimately show
    "η: x ↦ DtoKM⋅x"
    "η: x ↦ x' ⟹ x = KMtoD⋅x'"
      by (simp_all add: ValD_copy_lub_ID)
qed

(*>*)
text‹

Theorem 2 from citet‹"DBLP:conf/icalp/Reynolds74"›.

›

theorem Theorem2: "evalD e⋅ρ = KMtoD⋅(evalK e⋅(DtoKM oo ρ))"
using Lemma1(2)[OF Theorem1] Lemma1(1) by (simp add: cfcomp1)

end

text‹

citet‹‹Remark~48› in "DBLP:journals/tcs/Filinski07"› observes that there
will not be a retraction between direct and continuation semantics for
languages with richer notions of effects.

It should be routine to extend the above approach to the higher-order
backtracking language of citet‹"DBLP:conf/icfp/WandV04"›.

I wonder if it is possible to construct continuation semantics from
direct semantics as proposed by
citet‹"DBLP:journals/jacm/SethiT80"›. Roughly we might hope to lift a
retraction between two value domains to a retraction at higher types
by synthesising a suitable logical relation.

›
(*<*)

end
(*>*)