Search code examples
coqdependent-type

Trouble in implementing dependently typed lookup in Coq using Equations


I'm trying to use Equations package to define a function over vectors in Coq. The minimum code that shows the problem that I will describe is available at the following gist.

My idea is to code a function that does a lookup on a "proof" that some type holds for all elements of a vector, which has a standard definition:

  Inductive vec (A : Type) : nat -> Type :=
  | VNil  : vec A 0
  | VCons : forall n, A -> vec A n -> vec A (S n).

Using the previous type, I had defined the following (also standard) lookup operation (using Equations):

   Equations vlookup {A}{n}(i : fin n) (v : vec A n) : A :=
      vlookup  FZero (VCons x _) := x ;
      vlookup  (FSucc ix) (VCons _ xs) := vlookup ix xs.

Now, the trouble begins. I want to define the type of "proofs" that some property holds for all elements in a vector. The following inductive type does this job:

   Inductive vforall {A : Type}(P : A -> Type) : forall n, vec A n -> Type :=
   | VFNil  : vforall P _ VNil
   | VFCons : forall n x xs,
         P x -> vforall P n xs -> vforall P (S n) (VCons x xs).

Finally, the function that I want to define is

Equations vforall_lookup
            {n}
            {A : Type}
            {P : A -> Type}
            {xs : vec A n}
            (idx : fin n) :
            vforall P xs -> P (vlookup idx xs) :=
    vforall_lookup FZero (VFCons _ _ pf _) := pf ;
    vforall_lookup (FSucc ix) (VFCons _ _ _ ps) := vforall_lookup ix ps.

At leas to me, this definition make sense and it should type check. But, Equations had showed the following warning and left me with a proof obligation in which I had no idea on how to finish it.

The message presented after the definition of the previous function is:

  Warning:
  In environment
  eos : end_of_section
  fix_0 : forall (n : nat) (A : Type) (P : A -> Type) (xs : vec A n) 
                 (idx : fin n) (v : vforall P xs),
                  vforall_lookup_ind n A P xs idx v (vforall_lookup idx v)
  A : Type
  P : A -> Type
  n0 : nat
  x : A
  xs0 : vec A n0
  idx : fin n0
  p : P x
  v : vforall P xs0
  Unable to unify "VFCons P n0 x xs0 p v" with "v".

The obligation left is

  Obligation 1 of vforall_lookup_ind_fun:
  (forall (n : nat) (A : Type) (P : A -> Type) (xs : vec A n) 
      (idx : fin n) (v : vforall P xs),
  vforall_lookup_ind n A P xs idx v (vforall_lookup idx v)).

Later, after looking at a similar definition in Agda standard library, I realised that the previous function definition is missing a case for the empty vector:

  lookup : ∀ {a p} {A : Set a} {P : A → Set p} {k} {xs : Vec A k} →
          (i : Fin k) → All P xs → P (Vec.lookup i xs)
  lookup ()      []
  lookup zero    (px ∷ pxs) = px
  lookup (suc i) (px ∷ pxs) = lookup i pxs

My question is, how can I specify that, for the empty vector case, the right hand side should be empty, i.e. a contradiction? The Equations manual shows an example for equality but I could adapt it to this case. Any idea on what am I doing wrong?


Solution

  • I think I finally understood what is going on in this example by looking closely at the obligation generated.

    The definition is correct, and it is accepted (you can use vforall_lookup without solving the obligation). What fails to be generated is the induction principle associated to the function.

    More precisely, Equations generates the right induction principle in three steps (this is detailed in the reference manual) in section "Elimination principle":

    • it generates the graph of the function (in my version of Equations it is called vforall_lookup_graph, in previous versions it was called vforall_lookup_ind). I am not sure that I fully understand what it is. Intuitively, it reflects the structure of the body of the function. In any case, it is a key component to generate the induction principle.

    • it proves that the function respects this graph (in a lemma called vforall_lookup_graph_correct or vforall_lookup_ind_fun);

    • it combines the last two results to generate the induction principle associated to the function (this lemma is called vforall_lookup_elim in all versions).

    In your case, the graph was correctly generated but Equations was not able to prove automatically that the function respects its graph (step 2), so it is left to you.

    Let's give it a try!

    Next Obligation.
      induction v.
      - inversion idx.
      - dependent elimination idx.
        (* a powerful destruct provided by Equations
           that correctly working with dependent types
        *)
        + constructor.
        + constructor.
    

    Coq rejects the last call to constructor with the error

    Unable to unify "VFCons P n1 x xs p v" with "v".
    

    This really looks like the error obtained in the first place, so I think the automatic resolution reached this same point and failed. Does this mean that we took a wrong path? Let's look closer at the goal before the second constructor.

    We have to prove

    vforall_lookup_graph (S n1) A P (VCons x xs) (FSucc f) (VFCons P n1 x xs p v) (vforall_lookup (FSucc f) (VFCons P n1 x xs p v))
    

    while the type of vforall_lookup_graph_equation_2, the second constructor of vforall_lookup_graph_equation is

    forall (n : nat) (A : Type) (P : A -> Type) (x : A) (xs0 : vec A n) (f : fin n) (p : P x) (v : vforall P xs0),
      vforall_lookup_graph n A P xs0 f v (vforall_lookup f v) -> vforall_lookup_graph (S n) A P (VCons x xs0) (FSucc f) (VFCons P n x xs0 p v) (vforall_lookup f v)
    

    The difference lies in the calls to vforall_lookup. In the first case, we have

    vforall_lookup (FSucc f) (VFCons P n1 x xs p v)
    

    and in the second case

    vforall_lookup f v
    

    But these are identical by definition of vforall_lookup! But by default the unification fails to recognize that. We need to help it a bit. We can either give the value of some argument, e.g.

    apply (vforall_lookup_graph_equation_2 n0).
    

    or we can use exact or refine that unify more aggressively than apply since they are given the whole term and not only its head

    refine (vforall_lookup_graph_equation_2 _ _ _ _ _ _ _ _ _).
    

    We can conclude easily by the induction hypothesis. This gives the following proof

    Next Obligation.
      induction v.
      - inversion idx.
      - dependent elimination idx.
        + constructor.
        + (* IHv is the induction hypothesis *)
          exact (vforall_lookup_graph_equation_2 _ _ _ _ _ _ _ _ (IHv _)).
    Defined.
    

    Since I like doing proofs with dependent types by hand, I can't help giving a proof that does not use dependent elimination.

    Next Obligation.
      induction v.
      - inversion idx.
      - revert dependent xs.
        refine (
          match idx as id in fin k return
            match k return fin k -> Type with
            | 0 => fun _ => IDProp
            | S n => fun _ => _
            end id
          with
          | FZero => _
          | FSucc f => _
          end); intros.
        + constructor.
        + exact (vforall_lookup_graph_equation_2 _ _ _ _ _ _ _ _ (IHv _)).
    Defined.