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
Related
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 am following the book Computational Type Theory and Interactive Theorem Proving with Coq, and
one of the exercises is for me to write of term of type:
forall (p:bool -> Prop) (x:bool), (x = true -> p true) -> (x = false -> p false) -> p x
I tried the obvious:
Fail Definition L7 : forall (p:bool -> Prop) (x:bool), (x = true -> p true) -> (x = false -> p false) -> p x :=
fun (p:bool -> Prop) =>
fun (x:bool) =>
fun (tt:x = true -> p true) =>
fun (ff:x = false -> p false) =>
match x with
| true => tt (eq_refl true)
| false => ff (eq_refl false)
end.
and the less obvious:
Definition bool_dec : forall (x:bool), x = true \/ x = false :=
fun (x:bool) =>
match x with
| true => or_introl (eq_refl true)
| false => or_intror (eq_refl false)
end.
Fail Definition L8 : forall (p:bool -> Prop) (x:bool), (x = true -> p true) -> (x = false -> p false) -> p x :=
fun (p:bool -> Prop) =>
fun (x:bool) =>
fun (tt:x = true -> p true) =>
fun (ff:x = false -> p false) =>
match bool_dec x with
| or_introl p => tt p
| or_intror p => ff p
end.
I know there is going to be a trick match ... in ... return ... or some convoy pattern business, leading to a duh moment on my part, but I have been spending an hour on this and would like to move on. Can anyone take me out of my misery?
First, you can use keyword fun just once in nested function like this
fun (p:bool -> Prop)
(x:bool)
(tt:x = true -> p true)
(ff:x = false -> p false) =>
match x with
| true => tt (eq_refl true)
| false => ff (eq_refl false)
end.
Now, a good way would be use tactics to generate a proof (an object of this type), then use Print to see what is the object.
Theorem L7 : forall (p:bool -> Prop) (x:bool), (x = true -> p true) -> (x = false -> p false) -> p x.
Proof.
intros. destruct x.
- apply H. reflexivity.
- apply H0. reflexivity.
Qed.
Print L7.
The output would be
L7 =
fun (p : bool -> Prop)
(x : bool)
(H : x = true -> p true)
(H0 : x = false -> p false)
=>
(if x as b
return
((b = true -> p true) ->
(b = false -> p false) ->
p b)
then
fun
(H1 : true = true -> p true)
(_ : true = false ->
p false) =>
H1 eq_refl
else
fun
(_ : false = true -> p true)
(H2 : false = false ->
p false) =>
H2 eq_refl) H H0
: forall
(p : bool -> Prop)
(x : bool),
(x = true -> p true) ->
(x = false -> p false) ->
p x
Arguments L7 _%function_scope
_%bool_scope (_
_)%function_scope
Thanks to Kamyar above, I was able to obtain a simple solution:
Definition L8 : forall (p:bool -> Prop) (x:bool), (x = true -> p true) -> (x = false -> p false) -> p x :=
fun (p:bool -> Prop) (x:bool) (H1:x = true -> p true) (H2:x = false -> p false) =>
match x as b return x = b -> p b with
| true => H1
| false => H2
end (eq_refl x).
In my program, I use List.filter to search a list for finding specific elements. I am proving if List.filter finds some elements in a list, then by appenindg another list we still get those elements that were in the first list before appending. I am a bit stuck in provingfilterKeepSameElementsAfterAppending. To make my program shorter, I changed my program's data to customType and mydata.
Require Import List Nat.
Inductive customType : Type :=
|Const1: nat -> customType
|Const2: list nat -> customType.
Inductive mydata : Set :=
|Set1: customType * customType ->mydata
|Set2: customType ->mydata.
Fixpoint custome_Equal (c1 c2:customType) :bool:=
match c1 with
|Const1 nt => match c2 with
|Const1 mt => eqb nt mt
|Const2 (hm::lmt) => eqb nt hm
| _ => false
end
|Const2 (hn::lnt) => match c2 with
|Const1 mt => eqb hn mt
|Const2 (hm:: lmt) => eqb hn hm
| _ => false
end
| _ => false
end.
Fixpoint Search (l: mydata) (t:customType): bool :=
match l with
|Set1 (a1, a2) => if (custome_Equal a2 t) then true else false
| _=>false
end.
Lemma filterKeepSameElementsAfterAppending(l1 l2: list mydata)(x:mydata)(ta:customType):
In x (filter (fun n => Search n ta) (l1)) -> In x (filter (fun n => Search n ta) (l2++ l1)).
Proof.
intro.
The filter_cat lemma should provide you some inspiration:
filter_cat
forall (T : Type) (a : pred T) (s1 s2 : seq T),
[seq x <- s1 ++ s2 | a x] = [seq x <- s1 | a x] ++ [seq x <- s2 | a x]
together with mem_cat should do what you want:
mem_cat
forall (T : eqType) (x : T) (s1 s2 : seq T),
(x \in s1 ++ s2) = (x \in s1) || (x \in s2)
Complete code:
From mathcomp Require Import all_ssreflect.
Lemma mem_filter_cat (T : eqType) p (l1 l2 : seq T) x :
x \in filter p l1 -> x \in filter p (l1 ++ l2).
Proof. by rewrite filter_cat mem_cat => ->. Qed.
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.
I have the following definition for terms :
Require Import Coq.Arith.Arith.
Require Import Coq.Lists.List.
Require Import Coq.Strings.String.
Import ListNotations.
Definition VarIndex:Type := nat.
Inductive Term : Type :=
|Var : VarIndex -> Term
|Comb: string -> (list Term) -> Term.
(*compare two list *)
Fixpoint beq_list {X:Type} (l l' :list X) (EqX :X -> X -> bool): bool :=
match l with
| [] => match l' with
| [] => true
| a => false
end
| (x::xs) =>
match l' with
|[] => false
|(y::ys) => if (EqX x y) then beq_list xs ys EqX else false
end
end.
Fixpoint length {X : Type} (l : list X) : nat :=
match l with
| nil => 0
| cons _ l' => S (length l')
end.
and a function beq_term to compare two terms define as follow :
Fixpoint beq_term (t1:Term) (t2:Term) : bool :=
match t1, t2 with
| Var i, Var j => beq_nat i j
| Var _, Comb _ _ => false
|Comb _ _, Var _ => false
|(Comb s1 ts1), Comb s2 ts2 => if(beq_nat (length ts1) (length ts2))
then beq_list ts1 ts2 beq_term
else false
end.
The definition of the function beq_term yields the error message:
Error: Cannot guess decreasing argument of fix.
So I am interested in how to convince Coq of the termination.
If you want to be able to use Coq's syntactic check in this simple example in particular, it is enough to write beq_list and beq_term into a single function.
Fixpoint beq_list (l l' :list Term) : bool :=
match l, l' with
| [], [] => true
| (Var i)::xs, (Var j)::ys => beq_nat i j && beq_list xs ys
| (Comb s1 ts1)::xs, (Comb s2 ts2)::ys => beq_list xs ys
| _,_ => false
end.