Require Import MyTactics.
Require Import FixExtra.
Require Import LambdaCalculusSyntax.
Require Import LambdaCalculusValues.

(* This is a definition of the CPS transformation. *)

(* This CPS transformation is "one-pass" in the sense that it does not produce
   any administrative redexes. (In other words, there is no need for a second
   pass, whose purpose would be to remove administrative redexes.)

   To achieve this, instead of defining [cps t k], where the continuation [k]
   is a term, we define [cps t c], where the continuation [c] is either a term
   (also known as an object-level continuation) or a term-with-a-hole [K]
   (also known as a meta-level continuation).

   This formulation of the CPS transformation is analogous to Danvy and
   Filinski's higher-order formulation. Yet, it is still technically
   first-order, because we represent a term-with-a-hole [K] as a term,
   where the variable 0 denotes the hole. *)

(* This CPS transformation is defined by recursion on the size of terms. This
   allows recursive calls of the form [cps (lift 1 t)], which would be illegal
   if [cps] was defined by structural induction. Definitions by well-founded
   recursion in Coq are somewhat tricky, requiring the use of the fixed point
   combinator [Fix] and the tactic [refine]. For explanations, see the chapter
   on general recursion in Chlipala's book at
   http://adam.chlipala.net/cpdt/html/GeneralRec.html *)

(* The situation could be complicated by the fact that we wish to define two
   functions simultaneously:

     [cpsv v] is the translation of a value [v].

     [cps t c] is the translation of a term [t] with continuation [c].

   To avoid this problem, we follow Danvy and Filinski's approach, which is to
   first define [cps] directly (as this does not cause much duplication), then
   define [cpsv] in terms of [cps]. In the latter step, no case analysis is
   required: [cpsv] is obtained by invoking [cps] with an identity meta-level
   continuation.

   Regardless of how [cps] and [cpsv] are defined, we prove that the they
   satisfy the desired recurrence equations, so, in the end, everything is
   just as if they had been defined in a mutually recursive manner. *)

(* -------------------------------------------------------------------------- *)

(* As explained above, a continuation [c] is

     either [O k], where [k] is a term (in fact, a value)
                   (an object-level continuation)

         or [M K], where [K] is term-with-a-hole
                   (a meta-level continuation).

   A term-with-a-hole [K] is represented as a term where the variable 0 denotes
   the hole (and, of course, all other variables are shifted up). *)

Inductive continuation :=
| O (k : term)
| M (K : term).

(* The term [apply c v] is the application of the continuation [c] to the
   value [v]. If [c] is an object-level continuation [k] (that is, a term),
   then it is just the object-level application [App k v]. If [c] is a
   meta-level continuation [K], then it is the meta-level operation of filling
   the hole with the value [v], which in fact is just a substitution,
   [K.[v/]]. *)

Definition apply (c : continuation) (v : term) : term :=
  match c with
  | O k =>
      App k v
  | M K =>
      K.[v/]
  end.

(* The term [reify c] is the reification of the continuation [c] as an
   object-level continuation (that is, a term). If [c] is an object-level
   continuation [k], then it is just [k]. If [c] is a meta-level continuation
   [K], then [reify c] is the term [\x.K x]. (This is usually known as a
   two-level eta-expansion.) Because the hole is already represented by the
   variable 0, filling the hole with the variable [x] is a no-op. Therefore,
   it suffices to write [Lam K] to obtain the desired lambda-abstraction. *)

Definition reify (c : continuation) : term :=
  match c with
  | O k =>
      k
  | M K =>
      Lam K
  end.

(* The continuation [substc sigma c] is the result of applying the
   substitution [sigma] to the continuation [c]. *)

Definition substc sigma (c : continuation) : continuation :=
  match c with
  | O k =>
      O k.[sigma]
  | M K =>
      M K.[up sigma]
  end.

(* [liftc i c] is the result of lifting the free names of the continuation [c]
   up by [i]. The function [liftc] can be defined in terms of [substc]. *)

Notation liftc i c :=
  (substc (ren (+i)) c).

(* -------------------------------------------------------------------------- *)

(* Here is the definition of [cps]. Because we must keep track of sizes and
   prove that the recursive calls cause a decrease in the size, this
   definition cannot be easily written as Coq terms. Instead, we switch to
   proof mode and use the tactic [refine]. This allows us to write some of the
   code, with holes [_] in it, and use proof mode to fill the holes. *)

(* [cps t c] is the CPS-translation of the term [t] with continuation [c]. *)

Definition cps : term -> continuation -> term.
Proof.
  (* The definition is by well-founded recursion on the size of [t]. *)
  refine (Fix smaller_wf_transparent (fun _ => continuation -> term) _).
  (* We receive the arguments [t] and [c] as well as a function [cps_]
     which we use for recursive calls. At every call to [cps_], there
     is an obligation to prove that the size of the argument is less
     than the size of [t]. *)
  intros t cps_ c.
  (* The definition is by cases on the term [t]. *)
  destruct t as [ x | t | t1 t2 | t1 t2 ].
  (* When [t] is a value, we return an application of the continuation [c]
     to a value which will later be known as [cpsv t]. *)
  (* Case: [Var x]. *)
  { refine (apply c (Var x)). }
  (* Case: [Lam t]. *)
  (* In informal notation, the term [\x.t] is transformed to an application of
     [c] to [\x.\k.[cps t k]], where [k] is a fresh variable. Here, [k] is
     represented by the de Bruijn index 0, and the term [t] must be lifted
     because it is brought into the scope of [k]. *)
  { refine (apply c
      (Lam (* x *) (Lam (* k *) (cps_ (lift 1 t) _ (O (Var 0)))))
    ); abstract size. }
  (* Case: [App t1 t2]. *)
  (* The idea is, roughly, to first obtain the value [v1] of [t1], then obtain
     the value [v2] of [t2], then perform the application [v1 v2 c].

     Two successive calls to [cps] are used to obtain [v1] and [v2]. In each
     case, we avoid administrative redexes by using an [M] continuation.
     Thus, [v1] and [v2] are represented by two holes, denoted by the
     variables [Var 1] and [Var 0].

     If [t1] is a value, then the first hole will be filled directly with (the
     translation of) [t1]; otherwise, it will be filled with a fresh variable,
     bound to the result of evaluating (the translation of) [t1]. The same
     goes for [t2].

     The application [v1 v2 c] does not directly make sense, since [c] is a
     continuation, not a term. Instead of [c], we must use [reify c]. The
     continuation [c] must turned into a term, so it can be used in this
     term-level application.

     A little de Bruijn wizardry is involved. The term [t2] must be lifted up
     by 1 because it is brought into the scope of the first meta-level
     continuation. Similarly, the first hole must be lifted by 1 because it is
     brought into the scope of the second meta-level continuation: thus, it
     becomes Var 1. Finally, the continuation [c] must be lifted up by [2]
     because it is brought into the scope of both. Here, we have a choice
     between [reify (liftc _ c)] and [lift _ (reify c)], which mean the same
     thing. *)
  { refine (
      cps_ t1 _ (M (
        cps_ (lift 1 t2) _ (M (
          App (App (Var 1) (Var 0)) (lift 2 (reify c))
        ))
      ))
    );
    abstract size.
  }
  (* Case: [Let x = t1 in t2]. *)
  (* The idea is to first obtain the value [v1] of [t1]. This value is
     represented by the hole [Var 0] in the [M] continuation. We bind
     this value via a [Let] construct to the variable [x] (represented by the
     index 0 in [t2]), then execute [t2], under the original continuation [c].
     All variables in [t2] except [x] must lifted up by one because they are
     brought in the scope of the meta-level continuation. The continuation [c]
     must be lifted up by 2 because it is brought in the scope of the
     meta-level continuation and in the scope of the [Let] construct. *)
  { refine (
      cps_ t1 _ (M (
        Let (Var 0) (
          cps_ t2.[up (ren (+1))] _ (liftc 2 c)
        )
      ))
    );
    abstract size.
  }
Defined.

(* -------------------------------------------------------------------------- *)

(* The above definition can be used inside Coq to compute the image of a term
   through the transformation. For instance, the image under [cps] of [\x.x]
   with object-level continuation [k] (a variable) is [k (\x.\k.k x)]. *)

Goal
  cps (Lam (Var 0)) (O (Var 0)) =
  App (Var 0) (Lam (Lam (App (Var 0) (Var 1)))).
Proof.
  compute. (* optional *)
  reflexivity.
Qed.

(* The image of [(\x.x) y] with continuation [k] is [(\x.\k.k x) y k]. *)

Goal
  cps (App (Lam (Var 0)) (Var 0)) (O (Var 1)) =
  App (App (Lam (Lam (App (Var 0) (Var 1)))) (Var 0)) (Var 1).
Proof.
  compute. (* optional *)
  reflexivity.
Qed.

(* -------------------------------------------------------------------------- *)

(* The initial continuation is used when invoking [cps] at the top level. *)

(* We could use [O (Lam (Var 0))] -- that is, the identity function -- as
   the initial continuation. Instead, we use [M (Var 0)], that is, the
   empty context. This sometimes saves one beta-redex. *)

Definition init :=
  M (Var 0).

Definition cpsinit t :=
  cps t init.

(* -------------------------------------------------------------------------- *)

(* Now that [cps] is defined, we can define [cpsv] in terms of it. *)

(* We explicitly check whether [v] is a value, and if it is not, we return a
   dummy closed value. [cpsv] is supposed to be applied only to values,
   anyway. Using a dummy value allows us to prove that [cpsv v] is always a
   value, without requiring that [v] itself be a value. *)

Definition cpsv (v : term) :=
  if_value v
    (cpsinit v)
    (Lam (Var 0)).

(* -------------------------------------------------------------------------- *)

(* The function [cps] satisfies the expected recurrence equations. *)

(* The lemmas [cps_var] and [cps_lam] are not used outside this file, as we
   use [cps_value] instead, followed with [cpsv_var] or [cpsv_lam]. *)

Lemma cps_var:
  forall x c,
  cps (Var x) c = apply c (Var x).
Proof.
  reflexivity.
Qed.

Lemma cps_lam:
  forall t c,
  cps (Lam t) c =
    apply c (Lam (* x *) (Lam (* k *) (cps (lift 1 t) (O (Var 0))))).
Proof.
  intros. erewrite Fix_eq_simplified with (f := cps) by reflexivity.
  reflexivity.
Qed.

Lemma cps_app:
  forall t1 t2 c,
  cps (App t1 t2) c =
    cps t1 (M (
      cps (lift 1 t2) (M (
        App (App (Var 1) (Var 0)) (lift 2 (reify c))
      ))
    )).
Proof.
  intros. erewrite Fix_eq_simplified with (f := cps) by reflexivity.
  reflexivity.
Qed.

Lemma cps_let:
  forall t1 t2 c,
  cps (Let t1 t2) c =
    cps t1 (M (
      Let (Var 0) (
        cps t2.[up (ren (+1))] (liftc 2 c)
      )
    )).
Proof.
  intros. erewrite Fix_eq_simplified with (f := cps) by reflexivity.
  reflexivity.
Qed.

(* -------------------------------------------------------------------------- *)

(* The translation of values is uniform: we also have the following equation. *)

Lemma cps_value:
  forall v c,
  is_value v ->
  cps v c = apply c (cpsv v).
Proof.
  destruct v; simpl; intros; try not_a_value; unfold cpsv, cpsinit.
  { rewrite !cps_var. reflexivity. }
  { rewrite !cps_lam. reflexivity. }
Qed.

(* -------------------------------------------------------------------------- *)

(* The function [cpsv] satisfies the expected recurrence equations.  *)

Lemma cpsv_var:
  forall x,
  cpsv (Var x) = Var x.
Proof.
  reflexivity.
Qed.

Lemma cpsv_lam:
  forall t,
  cpsv (Lam t) = Lam (Lam (cps (lift 1 t) (O (Var 0)))).
Proof.
  intros. unfold cpsv, cpsinit. rewrite cps_lam. reflexivity.
Qed.

(* If [theta] is a renaming, then [theta x] is a variable, so [cpsv (theta x)]
   is [theta x], which can also be written [(Var x).[theta]]. *)

Lemma cpsv_var_theta:
  forall theta x,
  is_ren theta ->
  cpsv (theta x) = (Var x).[theta].
Proof.
  inversion 1. subst. asimpl. reflexivity.
Qed.

(* -------------------------------------------------------------------------- *)

(* The tactic [cps] applies the rewriting rules [cps_value] and [cps_app] as
   many times as possible, therefore expanding applications of the function
   [cps] to values and to applications. *)

Ltac cps :=
  repeat first [ rewrite cps_value by obvious
               | rewrite cps_app | rewrite cps_let ].

(* -------------------------------------------------------------------------- *)

(* The translation of a value is a value. *)

(* In fact, thanks to the manner in which we have defined [cpsv], the image of
   every term through [cpsv] is a value. This turns out to be quite pleasant,
   as it allows removing nasty side conditions in several lemmas. *)

Lemma is_value_cpsv:
  forall v,
  (* is_value v -> *)
  is_value (cpsv v).
Proof.
  intros. destruct v; simpl; tauto.
Qed.

Hint Resolve is_value_cpsv : is_value obvious.

Hint Rewrite cpsv_var cpsv_lam : cpsv.
Ltac cpsv := autorewrite with cpsv.

(* -------------------------------------------------------------------------- *)

(* As a final step, we prove an induction principle that helps work with the
   functions [cpsv] and [cps]. When trying to establish a property of the
   function [cps], we often need to prove this property by induction on the
   size of terms. Furthermore, we usually need to state an auxiliary property
   of the function [cpsv] and to prove the two statements simultaneously, by
   induction on the size of terms. The following lemma is tailored for this
   purpose. It proves the properties [Pcpsv] and [Pcps] simultaneously. The
   manner in which the index [n] is used reflects precisely the manner in
   which each function depends on the other, with or without a decrease in
   [n]. The dependencies are as follows:

     [cpsv] invokes [cps] with a size decrease.

     [cps]  invokes [cpsv] without a size decrease and
                    [cps] with a size decrease.

   It is worth noting that this proof has nothing to do with the definitions
   of [cpsv] and [cps]. It happens to reflect just the right dependencies
   between them. *)

Lemma mutual_induction:
  forall
  (Pcpsv : term -> Prop)
  (Pcps : term -> continuation -> Prop),
  (forall n,
    (forall t c, size t < n -> Pcps t c) ->
    (forall v,   size v < S n -> Pcpsv v)
  ) ->
  (forall n,
    (forall v,   size v < S n -> Pcpsv v) ->
    (forall t c, size t < n -> Pcps t c) ->
    (forall t c, size t < S n -> Pcps t c)
  ) ->
  (
    (forall v,   Pcpsv v) /\
    (forall t c, Pcps t c)
  ).
Proof.
  intros Pcpsv Pcps IHcpsv IHcps.
  assert (fact:
    forall n,
    (forall v,   size v < n -> Pcpsv v) /\
    (forall t k, size t < n -> Pcps t k)
  ).
  { induction n; intros; split; intros;
    try solve [ elimtype False; omega ];
    destruct IHn as (?&?); eauto. }
  split; intros; eapply fact; eauto.
Qed.

(* -------------------------------------------------------------------------- *)

(* In the proofs that follow, we never expand the definition of [cpsv] or
   [cps]: we use the tactics [cpsv] and [cps] instead. We actually forbid
   unfolding [cpsv] and [cps], so our proofs cannot depend on the details of
   the above definitions.

   In general, when working with complex objects, it is good practice anyway
   to characterize an object and forget how it was defined. Here, the
   functions [cpsv] and [cps] are characterized by the equations that they
   satisfy; the rest does not matter.

   Attempting to work with transparent [cpsv] and [cps] would be problematic
   for several reasons. The tactics [simpl] and [asimpl] would sometimes
   expand these functions too far. Furthermore, because we have used the term
   [smaller_wf_transparent] inside the definition of [cps], expanding [cps]
   definition would often give rise to uncontrollably large terms. *)

Global Opaque cps cpsv.
