7

We have a function that inserts an element into a specific index of a list.

Fixpoint inject_into {A} (x : A) (l : list A) (n : nat) : option (list A) :=
  match n, l with
    | 0, _        => Some (x :: l)
    | S k, []     => None
    | S k, h :: t => let kwa := inject_into x t k
                     in match kwa with
                          | None => None
                          | Some l' => Some (h :: l')
                        end
  end.

The following property of the aforementioned function is of relevance to the problem (proof omitted, straightforward induction on l with n not being fixed):

Theorem inject_correct_index : forall A x (l : list A) n,
  n <= length l -> exists l', inject_into x l n = Some l'.

And we have a computational definition of permutations, with iota k being a list of nats [0...k]:

Fixpoint permute {A} (l : list A) : list (list A) :=
  match l with
    | []     => [[]]
    | h :: t => flat_map (
                  fun x => map (
                             fun y => match inject_into h x y with
                                        | None => []
                                        | Some permutations => permutations
                                      end
                           ) (iota (length t))) (permute t)
  end.

The theorem we're trying to prove:

Theorem num_permutations : forall A (l : list A) k,
  length l = k -> length (permute l) = factorial k.

By induction on l we can (eventually) get to following goal: length (permute (a :: l)) = S (length l) * length (permute l). If we now simply cbn, the resulting goal is stated as follows:

length
  (flat_map
     (fun x : list A =>
      map
        (fun y : nat =>
         match inject_into a x y with
         | Some permutations => permutations
         | None => []
         end) (iota (length l))) (permute l)) =
length (permute l) + length l * length (permute l)

Here I would like to proceed by destruct (inject_into a x y), which is impossible considering x and y are lambda arguments. Please note that we will never get the None branch as a result of the lemma inject_correct_index.

How does one proceed from this proof state? (Please do note that I am not trying to simply complete the proof of the theorem, that's completely irrelevant.)

ScarletAmaranth
  • 5,065
  • 2
  • 23
  • 34
  • A [mcve] with all the imports would help us to help you :) – Anton Trunov May 08 '17 at 14:26
  • @AntonTrunov It would be possible to make it slightly more minimal, hopefully 2 definitions is within reason. – ScarletAmaranth May 08 '17 at 15:34
  • 1
    You may want to rewrite with `Lemma eq_map X Y (f g : X -> Y) l : (forall x, f x = g x) -> map f l = map g l. Proof. intros h_eq; induction l as [|x l ihl]; [easy|]. now simpl; rewrite h_eq, ihl. Qed. ` and similarly for `flat_map`. This way, you can assert the needed extensional equality. – ejgallego May 08 '17 at 18:03

1 Answers1

11

There is a way to rewrite under binders: the setoid_rewrite tactic (see §27.3.1 of the Coq Reference manual).

However, direct rewriting under lambdas is not possible without assuming an axiom as powerful as the axiom of functional extensionality (functional_extensionality).

Otherwise, we could have proved:

(* classical example *)
Goal (fun n => n + 0) = (fun n => n).
  Fail setoid_rewrite <- plus_n_O.
Abort.

See here for more detail.

Nevertheless, if you are willing to accept such axiom, then you can use the approach described by Matthieu Sozeau in this Coq Club post to rewrite under lambdas like so:

Require Import Coq.Logic.FunctionalExtensionality.
Require Import Coq.Setoids.Setoid.
Require Import Coq.Classes.Morphisms.

Generalizable All Variables.

Instance pointwise_eq_ext {A B : Type} `(sb : subrelation B RB eq)
  : subrelation (pointwise_relation A RB) eq.
Proof. intros f g Hfg. apply functional_extensionality. intro x; apply sb, (Hfg x). Qed.

Goal (fun n => n + 0) = (fun n => n).
  setoid_rewrite <- plus_n_O.
  reflexivity.
Qed.
Anton Trunov
  • 15,074
  • 2
  • 23
  • 43
  • I find the answer helpful. I ended up using the setoid_rewrite. Sadly there's no setoid_destruct (not that it makes any sense...). – ScarletAmaranth May 08 '17 at 18:26
  • Thanks! Btw, if it were up to me then in your *particular* situation I'd settle for the approach @ejgallego pointed out. – Anton Trunov May 08 '17 at 18:31
  • I have tried to reconstruct this - what exactly did you setoid_rewrite with? It seems that the only thing that would help here is to actually destruct the `match inject_into h x y` but as @ScarletAmaranth has pointed out, `setoid_destruct` is non sequitur. – AntlerM May 11 '17 at 08:22
  • 1
    @AntlerM Does [this gist](https://gist.github.com/anton-trunov/8d4a1360bee9d1c160c6de0bd41d2147) work for you? – Anton Trunov May 11 '17 at 10:24
  • @AntonTrunov It does, thanks a lot! I had to read up about the Instance shenenigans, one does not simply setoid_rewrite right away. Thanks again. – AntlerM May 11 '17 at 14:31
  • @AntlerM Sorry for the confusion, I mislead Anton into believing that `forall x y, inject_into a x y = Some x`; of course what you're supposed to have is `forall x y, y <= length x -> exists x', inject_into a x y = Some x'`, as per the correct_index lemma. To make inject_into total with `n : nat` and not some kind of horrible dependent `Fin (length l)`, the definition just returns `None` without proof if the index used for insertion is silly. – ScarletAmaranth May 12 '17 at 15:05
  • @ScarletAmaranth Oh ok, but then I cannot rewrite with something of form `exists x, P(x) = P'(X)`. – AntlerM May 17 '17 at 06:22