2

I'm trying to do something in Coq similar to this liquid Haskell trick, which defines a partial function but proves it's actually total:

{-@ head :: {xs:[a] | len xs > 0} -> a @-}
head (x:xs) = x

Here is my first attempt, but Coq doesn't like it ("Casts are not supported in this pattern."):

Definition safeHead {A : Type} (xs : list A) (nonempty : xs <> nil) : A.
Proof.
  refine (fun {A : type} (xs : list A) (pf : xs <> nil) =>
            match xs with
            | x : xs => x
            | nil => _
            end).
  (* Some tactic here to prove the hole from nonempty *)
Defined.

I also tried adapting option 1 from this blog post, but that fails with the same error:

Definition safeHead {A : Type} (xs : list A) (not_nil : xs <> nil) : A
  match xs return _ with
  | x : xs => fun _ => x
  | nil => fun is_nil => False_rect _ (not_nil is_nil)
  end eq_refl.

Is there a way to get this to work in coq? I'd also love to understand the error message; what is the 'cast' and in which pattern is it failing?

Felipe
  • 3,003
  • 2
  • 26
  • 44
  • 3
    The cast Coq complains about is the pattern `x : xs` : in Coq, `y : A` is a way to ascribe the type `A` to the term `y`. The constructor for adding an element onto a list is either `cons x xs` or `x :: xs` (the latter only if you issued the command `From Coq Require Import List. Import ListNotations.` to bring the list notation into scope). – kyo dralliam Jun 20 '21 at 07:41

2 Answers2

4

As @kyo dralliam pointed out, you should use :: and not : for the cons operation of lists.

The main problem with your definition is that you lose the information that xs is not empty when you match on it. To keep that information you must be explicit about it. Like so:

From Coq Require Import Utf8 List.
Import ListNotations.

Definition safeHead {A : Type} (xs : list A) (nonempty : xs ≠ []) : A :=
  match xs as l return l ≠ [] → A with
  | [] => ltac:(contradiction)
  | x :: xs => λ _, x
  end nonempty.

Using the return information of the match I say that I construct a proof of l ≠ [] → A where l is a local name I give to xs using the as clause.

This means that in the case [] we have to inhabit [] ≠ [] → A and thus I conclude using the contradiction tactic, using tactics in terms.

In the cons case x :: xs we don't really care about the non-empty assumption so I ignore the argument and return the head x as we wanted.

Finally the whole match expression now has type xs ≠ [] → A so to get A we must give it the nonempty proof.

Théo Winterhalter
  • 4,908
  • 2
  • 18
  • 34
0

One way to achive this is to use tactics to write the body of the definitions

Definition safeHead' {A : Type} (xs : list A) (not_nil : xs <> nil) : A.
  destruct xs.
  - exfalso; apply not_nil; reflexivity.
  - apply a.
Defined.

Let xs := cons 1 nil.
Definition xs_not_nil : xs <> nil.
  unfold xs; intro; discriminate.
Defined.

Eval compute in safeHead' xs xs_not_nil.

Another quite simple way to achive similar behavior is to define inductive datatype for non-empty list.

Inductive nlist {A : Type} : Type :=
  ncons (x : A) (xs : list A) : nlist.

Let nxs := ncons 1 nil.

Definition safeHead {A : Type} (nxs : nlist) : A :=
  match nxs with
  | ncons x xs => x
  end.

Eval compute in safeHead nxs. (* 1 : nat *)
Andrey
  • 712
  • 6
  • 16