I want to be able to compare two items of type "list" in Coq and get a boolean "true" or "false" for their equivalence.
Right now, I'm comparing the two lists this way:
Eval vm_compute in (list 1 = list 2).
I get a Prop of the form:
= nil
:: (2 :: 3 :: nil)
:: (2 :: nil)
:: (3 :: nil) :: nil =
nil
:: (2 :: 3 :: nil)
:: (2 :: nil)
:: (3 :: nil) :: nil
: Prop
Obviously list1 = list2, so how do I get it to just return true or false?
I use the Mathematical Components Library boolean equality operators:
From mathcomp Require Import all_ssreflect.
...
Eval vm_compute in list 1 == list 2
You can generate a boolean list equality function that takes as input a boolean equality over the elements automatically using Coq's commands:
Require Import Coq.Lists.List Coq.Bool.Bool.
Import Coq.Lists.List.ListNotations.
Scheme Equality for list.
This prints:
list_beq is defined
list_eq_dec is defined
where list_beq is a boolean equality function on lists that takes as first parameter a comparison function for the lists elements and then two lists:
Print list_beq.
Gives
list_beq =
fun (A : Type) (eq_A : A -> A -> bool) =>
fix list_eqrec (X Y : list A) {struct X} : bool :=
match X with
| [] => match Y with
| [] => true
| _ :: _ => false
end
| x :: x0 => match Y with
| [] => false
| x1 :: x2 => eq_A x x1 && list_eqrec x0 x2
end
end
: forall A : Type, (A -> A -> bool) -> list A -> list A -> bool
and
Check list_eq_dec
gives
list_eq_dec
: forall (A : Type) (eq_A : A -> A -> bool),
(forall x y : A, eq_A x y = true -> x = y) ->
(forall x y : A, x = y -> eq_A x y = true) -> forall x y : list A, {x = y} + {x <> y}
showing that list equality is decidable if the underlying types equality is agrees with leibniz equality.
Related
I have trees of arbitrary arity, with the following type:
Inductive Tree : Set :=
| Node : list Tree -> Tree.
I can easily create a number of Fixpoint functions such as the following one:
Fixpoint nodecount (tree: Tree ) : nat :=
match tree with
| Node trs => S (sum (map nodecount trs))
end.
but none of the following 'filter' type, even in trivial cases such as this one :
Function nodecount' (tree: Tree ) : nat :=
match tree with
| Node trs => S (sum (map nodecount' ( filter ( fun x => true) trs)))
end.
Whatever the function behind the filter is, the compiler rejects the function as ill-formed.
Is there a way to provide Coq with a proof that this class of filter functions actually produces well-behaved terms, so that any member of this class of fixpoints is allowed ?
Thank you for your help !
One solution is to use mapfilter : (A -> option B) -> list A -> list B instead of filter and a separate map. You also need to be careful in defining mapfilter so that the function argument is not part of the fix arguments.
This works because after unfolding mapfilter in nodecount', the recursive call to nodecount' will be syntactically a subterm of the input tree.
Definition mapfilter {A B : Type} (f : A -> option B) : list A -> list B :=
fix mapfilter_f (xs : list A) : list B :=
match xs with
| nil => nil
| x :: xs => match f x with
| Some y => y :: mapfilter_f xs
| None => mapfilter_f xs
end
end.
Fixpoint nodecount' (p : Tree -> bool) (tree: Tree ) : nat :=
match tree with
| Node trs => S (sum (mapfilter (fun x => if p x then Some (nodecount' p x) else None) trs))
end.
Alternatively you can directly write the list recursion into the tree one:
Fixpoint nodecount (f : Tree -> bool) (t : Tree) : nat :=
if (f t) then
match t with
| Node l =>
(fix iter l1 :=
match l1 with
| nil => 1
| (a :: l2)%list => nodecount f a + iter l2
end) l
end
else 0.
I have a heterogenous list as described in CPDT:
Section hlist.
Variable A : Type.
Variable B : A -> Type.
Inductive hlist : list A -> Type :=
| HNil : hlist nil
| HCons : forall (x : A) (ls : list A), B x -> hlist ls -> hlist (x :: ls)
.
End hlist.
and am trying to define a 'pointwise membership' predicate between a list of Ensembles and a list of elements:
Definition hlist_in_ensemble_hlist {A : Type}{B : A -> Type}(types : list A)
(sets : hlist A (fun a => Ensemble (B a)) types) (elems : hlist A B types) : Prop :=
match sets with
| HNil _ _ => True
| HCons _ _ a1 a1s b1 b1s =>
match elems with
| HNil _ _ => False
| HCons _ _ a2 a2s b2 b2s =>
Ensembles.In (B a1) b1 b2 (* /\ recursion (TODO) *)
end
end.
However, Coq complains about the Ensembles.In (B a1) b1 b2 part:
The term "b2" has type "B a2" while it is expected to have type "B a1"
Intuitively, a1 and a2 are the same, since they are the heads of the same types list. How do I communicate that to Coq? I tried matching elems with cons x xs and changing the offending line to Ensembles.In (B x) b1 b2, but that results in a similar error. I also read about the Convoy pattern, but am not sure how to apply it in this context.
This is a classic application of the convoy pattern of CPDT: when you need to argue that two indices are equal after pattern matching, you need to change the match so that it returns a function. In this case, I find it easier to perform the recursion on the hlist indices:
Require Import Coq.Lists.List.
Import ListNotations.
Section hlist.
Variable A : Type.
Variable B : A -> Type.
Inductive hlist : list A -> Type :=
| HNil : hlist nil
| HCons : forall (x : A) (ls : list A), B x -> hlist ls -> hlist (x :: ls)
.
Definition head x xs (l : hlist (x :: xs)) : B x :=
match l with
| HCons _ _ b _ => b
end.
Definition tail x xs (l : hlist (x :: xs)) : hlist xs :=
match l with
| HCons _ _ _ l => l
end.
End hlist.
Fixpoint hlist_in_ensemble_hlist {A : Type}{B : A -> Type}(types : list A)
: hlist A (fun a => B a -> Prop) types -> hlist A B types -> Prop :=
match types with
| [] => fun _ _ => True
| x :: xs => fun sets elems =>
head _ _ _ _ sets (head _ _ _ _ elems) /\
hlist_in_ensemble_hlist _ (tail _ _ _ _ sets) (tail _ _ _ _ elems)
end.
(Note that I have inlined the definition of Ensemble.)
I have the following code:
Here is the def of sorted:
Fixpoint sorted (l : list nat) :=
match l with
| [] => true
| x::xs => match xs with
| [] => true
| y :: ys => (x <=? y) && (sorted xs)
end
end.
Here is the def of insert:
Fixpoint insert (x : nat) (l : list nat) :=
match l with
| [] => [x]
| y::ys => if x <=? y then x :: l
else y :: insert x ys
end.
Here is the def of insert_spec:
Definition insert_spec (x : nat) (l : list nat) :=
sorted l ==> sorted (insert x l).
In insert_spec, what does "==>" mean?
It appears that you got the code from Software Foundations' QuickChick guide. Many (if not all) of the notations used in that guide can be found in the QuickChick Reference Manual. There, we find that "==>" is defined as a notation.
Module QcNotation.
Export QcDefaultNotation.
Notation "x ==> y" :=
(implication x y) (at level 55, right associativity)
: Checker_scope.
End QcNotation.
implication is a generic "is this implication true" parameter used by QuickChick.
Parameter implication :
∀ {prop : Type} `{Checkable prop} (b : bool) (p : prop), Checker.
Whenever the first argument is true, QuickChick tests that the second argument evaluates (in whatever context you're using QuickChick in) as true too.
So for your particular piece of code, "==>" is used to say that we want to test that whenever l is sorted, insert x l is sorted too.
With dependent types, it's possible to define an inductive type for sorted lists, e.g.:
data IsSorted : {a: Type} -> (ltRel: (a -> a -> Type)) -> List a -> Type where
IsSortedZero : IsSorted {a=a} ltRel Nil
IsSortedOne : (x: a) -> IsSorted ltRel [x]
IsSortedMany : (x: a) -> (y: a) -> .IsSorted rel (y::ys) -> .(rel x y) -> IsSorted rel (x::y::ys)
This can then be used to reason about sorted lists.
In Coq, one could also write a function Fixpoint is_sorted: {A: Type} (l: List A) : bool, and then make use of a type like is_sorted someList = true to prove things, by unfolding the definition of is_sorted. Is this latter approach possible in Idris, or does it only support the former approach?
Furthermore, for my own understanding: is the latter case an example of "proof by reflection", and is there any situation in which the latter approach would be preferable to the former?
I think the following partially does what you want (I will add the caveat that I have no experience of using Coq):
infixl 4 &
(&) : Bool -> Bool -> Bool
(&) True True = True
(&) _ _ = False
elim_and : x & y = True -> (x = True, y = True)
elim_and {x = False} {y = False} x_and_y_is_true = (x_and_y_is_true, x_and_y_is_true)
elim_and {x = False} {y = True} x_and_y_is_true = (x_and_y_is_true, Refl)
elim_and {x = True} {y = False} x_and_y_is_true = (Refl, x_and_y_is_true)
elim_and {x = True} {y = True} x_and_y_is_true = (Refl, Refl)
is_sorted : {a: Type} -> (ltRel: a -> a -> Bool) -> List a -> Bool
is_sorted ltRel [] = True
is_sorted ltRel (x :: []) = True
is_sorted ltRel (x :: y :: xs) = (ltRel x y) & (is_sorted ltRel (y :: xs))
is_sorted_true_elim : {x : a} -> is_sorted ltRel (x :: y :: xs) = True -> (ltRel x y = True,
is_sorted ltRel (y :: xs) = True)
is_sorted_true_elim {x} {y} {xs} {ltRel} is_sorted_x_y_xs = elim_and is_sorted_x_y_xs
The important detail is that if your function definition is a simple set of equations, then the unification will somewhat magically substitute one side of the equation for the other when required. (I used a less efficient non-shortcircuited version of the logical "and" operator, because the standard "&&" or "if/then/else" operators introduce complications of laziness.)
Ideally there should be some straightforward way to unfold definitions that include 'with'-based pattern matching, but I don't know how to make that work, eg:
is_sorted : {a: Type} -> (ltRel: a -> a -> Bool) -> List a -> Bool
is_sorted ltRel [] = True
is_sorted ltRel (x :: []) = True
is_sorted ltRel (x :: y :: xs) with (ltRel x y)
| True = is_sorted ltRel (y :: xs)
| False = False
is_sorted_true_elim : {x : a} -> is_sorted ltRel (x :: y :: xs) = True -> (ltRel x y = True,
is_sorted ltRel (y :: xs) = True)
is_sorted_true_elim {x} {y} {xs} {ltRel} is_sorted_x_y_xs with (ltRel x y) proof x_lt_y_value
| True = ?hole
| False = ?hole2
The list_rec function has the type:
list_rec
: forall (A : Type) (P : list A -> Set),
P nil ->
(forall (a : A) (l : list A), P l -> P (a :: l)%list) ->
forall l : list A, P l
In all of the examples I've come up with, P is just a constant function that ignores the input list and returns the same type no matter what. For example, P might be fun _ : list A => nat or fun _ : list A => list B. What are some use cases for making the output of P dependent on the input? Why is the type of P list A -> Set instead of just Set?
We can, for example, use list_rec with a non-constant P function to implement a function that converts a list to a vector (a length-indexed list).
Require List Vector.
Import List.ListNotations Vector.VectorNotations.
Set Implicit Arguments.
Section VecExample.
Variable A : Set.
Definition P (xs : list A) : Set := Vector.t A (length xs).
Definition list_to_vector : forall xs : list A, Vector.t A (length xs) :=
list_rec P [] (fun x _ vtail => x :: vtail).
End VecExample.
You can compare it with the standard definition of the Vector.of_list function, which does exactly the same (t means Vector.t in the following code), using explicit recursion instead of hiding it behind a recursion principle:
Fixpoint of_list {A} (l : list A) : t A (length l) :=
match l as l' return t A (length l') with
|Datatypes.nil => []
|(h :: tail)%list => (h :: (of_list tail))
end.
A simple test:
Eval compute in list_to_vector [1;2;3].
Eval compute in Vector.of_list [1;2;3].
Both function calls return the same result:
= [1; 2; 3]
: Vector.t nat (length [1; 2; 3])
Try to prove s ++ [] = s.
[Hint: Define P as fun s => s ++ [] = s.]