1

Disclaimer: I fear this post got quite long, however, I feel that in a smaller setup some valuable background information would be lost.

I am currently trying to change my formalisation to use the locally nameless representation by Charguéraud et al [1]. Apparently, this adaption is not as straightforward as I hoped because my definition of expressions contains lists (at least I currently think this is the main problem).

So, I have the following (minimal) definition of expressions.

Require Import Coq.Lists.List.
Require Import Coq.Arith.PeanoNat.

Parameter atom : Set.
Parameter eq_atom_dec : forall x y : atom, {x = y} + {x <> y}.

Definition VarIndex := nat.

Inductive Expr : Type :=
 | BVar : VarIndex -> VarIndex -> Expr
 | FVar : atom -> Expr
 | LetB : list Expr -> Expr -> Expr.

With this definition at hand I can define the opening operation.

Fixpoint open_rec (k: VarIndex) (u: list Expr) (e: Expr) :=
  match e with
  | BVar i j => if Nat.eq_dec k i then List.nth j u e else e
  | FVar x => e
  | LetB es e' => LetB (List.map (open_rec (S k) u) es) (open_rec (S k) u e')
  end.

Notation "{ k ~> u } t" := (open_rec k u t) (at level 67).

Definition open e u := open_rec 0 u e.

So far so good. Next the property of being "locally closed" is defined inductively as follows.

Inductive lc : Expr -> Prop :=
| lc_var : forall x,
    lc (FVar x)
| lc_let : forall (ts: list Expr) es e,
    Forall lc es ->
    lc (open e ts) ->
    lc (LetB es e).

The tutorial now states that we can proof a lemma about the interaction of lc and open, i.e. in a locally closed expressions nothing happens when we substitute a variable.

(* this is a auxiliary lemma that works just fine for me *)
Lemma open_rec_lc_core : forall e (j: VarIndex) v (i: VarIndex) u,
    i <> j ->
    {j ~> v} e = {i ~> u} ({j ~> v} e) ->
    e = {i ~> u} e.
Proof.
Admitted.

Lemma open_rec_lc0 : forall k u e,
    lc e ->
    e = {k ~> u} e.
Proof.
  intros k u e LC.
  generalize dependent k.
  induction LC; intro k.
  - reflexivity.
  - simpl.
    f_equal.
    + admit.
    + eapply open_rec_lc_core with (j := 0).
      * auto.
      * eapply IHLC.         
Admitted.

As you can see, there is a case that is "admitted" in the proof. The problem here is that I have to proof something about the let-bindings, but everything I have at hand is the following:

H : Forall lc (map (fun e' : Expr => open e' ts) es)
LC : lc (open e ts)
IHLC : forall k : VarIndex, open e ts = {k ~> u} open e ts

What I need is an equivalent hypothesis to IHLC but for es. My first guess was that I need to modify the induction principle as it is usually done[2] for inductive definitions with lists as arguments. However, I cannot workout a definition that actually type checks.

Fail Definition lc_ind2 :=
  fun (P : Expr -> Prop) (f : forall x : atom, P (FVar x))
    (f0 : forall (ts es : list Expr) (e : Expr),
        Forall lc (map (fun e' : Expr => open e' ts) es) ->
        lc (open e ts) -> P (open e ts) ->
        Forall P (map (fun e' => open e' ts ) es) ->
        P (LetB es e)) =>
    fix F (e : Expr) (l : lc e) {struct l} : P e :=
    match l in (lc e0) return (P e0) with
    | lc_var x => f x
    | lc_let ts es e0 f1 l0 =>
      f0 ts es e0 f1 l0 (F (open e0 ts) l0)
         ((fix F' (es: list Expr) : Forall P es :=
                     match es with
                     | nil => Forall_nil P
                     | cons x xs => Forall_cons x (F x _) (F' xs)
                     end) (map (fun e' => open e' ts) es))
    end.

Instead of _ in the application of Forall_cons I need something of type lc x, but I do not know how to come up with this value.

So, in the end my question is, if someone has an idea which definitions I need to modify in order to work with the LNR.

[1] Tutorial on LNR
[2] Induction principles with list arguments

ichistmeinname
  • 1,480
  • 10
  • 19
  • Oh, sorry, it's just a renaming for `nat`, but thanks for spotting that, I edited the post. – ichistmeinname Oct 11 '16 at 18:18
  • (1) Why does `BVar` have 2 indices? What do they mean informally? (2) What does `LetB` represent informally? – Anton Trunov Oct 14 '16 at 18:49
  • (1) is a consequence of (2). `LetB` represents multiple let-bindings à la "let x = 31; y = x + 2" and `BVar i j` has two indices to handle exactly these binders. The `i` says how many binders you have to pass in order to get to your declaration and in case of a let-binder the `j` says which of these multi-binders to take (for example, `1` for x and `2` for y in the above let-expression). The approach was also discussed in Chapter 7.2 by [Charguéraud](http://www.chargueraud.org/research/2009/ln/main.pdf). – ichistmeinname Oct 17 '16 at 07:46
  • @ichistmeinname I am interested by the proof of `open_rec_lc_core`, how did you prove it ? – eponier Oct 21 '16 at 17:06
  • @eponier Sorry for the late response; I created a [gist](https://gist.github.com/ichistmeinname/4f39ce693d566459ba580f7131f4d41f). – ichistmeinname Nov 01 '16 at 12:23
  • @ichistmeinname Thank you. I wanted to see if the proof required some trick about nested induction, but it is not the case. I don't understand really why. – eponier Nov 01 '16 at 21:20
  • @eponier Which part of the lemma did make you think of nested induction? – ichistmeinname Nov 02 '16 at 19:43
  • @ichistmeinname Nothing in particular. But it seemed to involve induction about `e`, which is a nested inductive type ; and the proof manipulates `Forall` and `map`, which often needs an induction. That's why I expected nested induction, and something complicated involving a `fix` inside a `fix`. Here, a normal induction inside a normal induction is sufficient. And I do not understand why it's enough. – eponier Nov 03 '16 at 15:14

2 Answers2

3

Okay, so in the end I just inlined the Forall into a local inductive definition that uses lc.

Inductive lc : Expr -> Prop :=
| lc_var : forall x,
    lc (FVar x)
| lc_let : forall (ts: list Expr) es e,
    Forall_lc es ->
    lc (open e ts) ->
    lc (LetB es e).
with Forall_lc : list Expr -> Prop :=
     | nil_lc : Forall_lc nil
     | cons_lc : forall e es, lc e -> Forall_lc es -> Forall_lc (e :: es).

And generated the induction principle I need.

Scheme lc2_ind := Minimality for lc Sort Prop
  with lc_Forall_ind := Minimality for Forall_lc Sort Prop.

The same approach was taken here (Chapter 4). I guess, in the end, the trick is to use mutually recursive definitions instead of trying to apply lc as a parameter to Forall.

ichistmeinname
  • 1,480
  • 10
  • 19
  • Actually I am trying to formalise a functional logic programming language, but the `let`-part is indeed just a functional component! : ) – ichistmeinname Oct 17 '16 at 11:36
  • i remember that i formalized the let with a list of pairs , each node represent a correspondence between a ident and a lamba term – Lilo Oct 17 '16 at 14:12
1

Here is a solution that works. I do not understand all the details. For instance, in the first proof, the induction must be done on the Forall hypothesis directly, not on es to respect the guard condition. Also note the use of refine, which lets build a term iteratively, by leaving underscores to yet unknown arguments and completing gradually.

Lemma lc_ind2 : forall P : Expr -> Prop,
  (forall x : atom, P (FVar x)) ->
  (forall (ts es : list Expr) (e : Expr),
  Forall lc es -> Forall P es ->
  lc (open e ts) -> P (open e ts) -> P (LetB es e)) ->
  forall e : Expr, lc e -> P e.
Proof.
  intros. revert e H1.
  refine (fix aux e H1 (* {struct H1} *) := match H1 with
  | lc_var x => H x
  | lc_let ts es e HFor Hlc => H0 ts es e HFor _ Hlc (aux (open e ts) Hlc)
  end).
  induction HFor.
  constructor.
  constructor.
  apply aux. apply H2. assumption.
Qed.

Lemma Forall_map : forall {A} f (l:list A),
  Forall (fun x => x = f x) l ->
  l = map f l.
Proof.
  intros.
  induction H.
  reflexivity.
  simpl. f_equal; assumption.
Qed.

Lemma open_rec_lc0 : forall k u e,
    lc e ->
    e = {k ~> u} e.
Proof.
  intros k u e H. revert k u.
  induction H using lc_ind2; intros.
  - reflexivity.
  - simpl. f_equal.
    + apply Forall_map. apply Forall_forall. rewrite Forall_forall in H0.
      intros. apply H0. assumption.
    + eapply open_rec_lc_core with (j := 0).
      * auto.
      * eapply IHlc.
Qed.
eponier
  • 3,062
  • 9
  • 20
  • I finally took the time to adapt your approach in my current setting and it works perfectly! I didn't know how `refine` works before, so thank you for introducing me to a new tactic. This is definitely helpful in the future as well. – ichistmeinname Nov 14 '16 at 08:03