Search code examples
pattern-matchingcoqdependent-typeconvoy-pattern

Pattern matching using information from theorems


I have the following question, look in the code.

  (* Suppose we have type A *)
  Variable A: Type.

  (* Also we have a function that returns the type (option A) *)
  Definition f_opt x: option A := ...

  (* Then, I can prove that this function always returns something: *)
  Theorem always_some: forall x, exists y, f_opt x = Some y.
  Admitted.

  (* Or, equivalently: *)
  Theorem always_not_none: forall x, f_opt x <> None.
  Admitted.

Now I would like to get a version of f_opt that always returns a value of type A. Something like this:

  Definition f x: A :=
    match f_opt x with
      | Some y => y
    end.

But I get the following error:

Non exhaustive pattern-matching: no clause found for pattern None.

I understand that I need to do some kind of work with types, but I do not understand what exactly i should do.


Solution

  • In Coq's underlying theory, every pattern matching must be exhaustive -- that is, it must explicitly consider all the constructors of the inductive type in question. This is why you are getting the error message you saw.

    How do we get around this restriction? There are a few solutions. First, let's see how to convince Coq that the None branch can never occur. For this, we will use your always_not_none theorem:

    Definition f x : A :=
      match f_opt x as res return res <> None -> A with
      | Some y => fun _ => y
      | None => fun H => match H eq_refl with end
      end (always_not_none x).
    

    This code might look strange at first sight, but it almost performs the pattern match you want. To explain to Coq that the None case never arises, it combines always_not_none with the fact that f_opt x = None on that branch to derive a contradiction. This is the H eq_refl term on that branch. Then, the match on that contradiction suffices to convince Coq that the branch is spurious. A bit more formally, because False, the contradictory proposition, is defined without any constructors, when we match on a term of type False, there are no branches to deal with, and the entire expression can return any type that we want -- in this case, A.

    What is strange about this code is the type annotations on the match, and that it returns a function instead of something of type A directly. This is done because of how dependent pattern match works in Coq: whenever we want to make use of information that we obtain from being in a particular branch of a match (here, that f_opt x is equal to None in that branch), we must explicitly make the match return a function -- what Adam Chlipala calls the convoy pattern. This is done so that Coq knows where you plan to use that extra information and check that it is done correctly. Here, we use that f_opt x is None to feed the hypothesis needed by always_not_none x to derive a contradiction.

    Although this will solve your problem, I would generally advise you against doing it this way. For instance, if you know that your A type is inhabited by some element a : A, then you can simply make f return a on that branch. This has the benefit of avoiding mentioning proofs inside your functions, which often gets in the way when simplifying and rewriting terms.