Is there a way to call a function recursively from tactic mode or from match expressions in Lean? - lean

Attempt #1:
def sget' {α : Type} {n : ℕ} (i : ℕ) {h1 : n > 0} {h2 : i < n} (s: sstack α n) : α :=
begin
cases n with n0 nn,
begin
have f : false, from nat.lt_asymm h1 h1,
tauto,
end,
induction s,
cases s_val,
begin
have : stack.empty.size = 0, from #stack_size_0 α,
tauto,
end,
cases i with i0 ri,
exact s_val_x,
exact sget' (pred i) s_val_s,
end
Attempt #2:
def sget' {α : Type} {n : ℕ} (i : ℕ) {h1 : n > 0} {h2 : i < n} (s: sstack α n) : α :=
match i, s with
| 0, ⟨stack.push x s, _⟩ := x
| i, ⟨stack.push _ s, _⟩ := sget' (pred i) ⟨s, _⟩
| _, ⟨stack.empty, _⟩ := sorry -- just ignore this
Lean in both cases throws unknown identifier sget' error. I know that I can call sget' recursively from ehh pattern guards (not sure how they are properly called), but is there any way to do something like that with tactics and/or match expressions?

You can do recursive calls if you use the equation compiler
def map (f : α → β) : list α → list β
| [] := []
| (a :: l) := f a :: map l
Otherwise you should use induction tactic or one of the explicit recursor functions (like nat.rec).

Related

Difference between let and have keywords in lean 4

I noticed that both
#eval have x : Nat := 3 ; x*2
and
#eval let x : Nat := 3 ; x*2
work in the same way. Same goes when proving theorems.
Are those equivalent? What is the difference between them?
The difference is that let "remembers" the definition and have forgets it.
So for example, the following works with let but not have.
example : {x : nat // x = 0} :=
let x := 0 in ⟨x, rfl⟩
In general have is usually used for proofs and let for everything else. In tactic mode you can use dsimp [x] to unfold the definition of let x := ...
This is what have does
Internally, the expression have h : p := s; t produces the term (fun (h : p) => t) s. In other words, s is a proof of p, t is a proof of the desired conclusion assuming h : p, and the two are combined by a lambda abstraction and application.
This is what let does
The let construct is a stronger means of abbreviation, and there are expressions of the form let a := t1; t2 that cannot be expressed as (fun a => t2) t1. As an exercise, try to understand why the definition of foo below type checks, but the definition of bar does not.
def foo := let a := Nat; fun x : a => x + 2
/-
def bar := (fun a => fun x : a => x + 2) Nat
-/

Combining two simple assumptions in Lean

I'm trying to construct this proof in Lean:
(P → Q) ∧ (R → ¬Q) → ¬(P ∧ R)
It feels like a simple proof by contradiction:
Assume P and R, the opposite of the conclusion.
Assume P → Q. Since P, Q.
Assume R → ¬Q. Since R, ¬Q.
Q and ¬Q. Contradiction.
Here's what I've got so far in Lean:
example (P Q R : Prop) : (P → Q) ∧ (R → ¬Q) → ¬(P ∧ R) :=
begin
assume a : (P → Q) ∧ (R → ¬Q),
assume b : P ∧ R,
cases a with pq rnq,
cases b with p r,
sorry
end
That leaves me with this goal:
P Q R : Prop,
pq : P → Q,
rnq : R → ¬Q,
p : P,
r : R
⊢ false
I feel like I should just be able to somehow combine p and pq to get Q, and combine r and rnq to get ¬Q. But I can't figure out how to do it. If I didn't have the false in the final goal, I could just apply pq p and it would be done.
Ignoring this particular proof, is there a way to combine two simple hypotheses into another simple hypothesis?
Is there a different way to approach this proof? Is my theorem just wrong in some way?
I think the tactic you're missing here is have. The have tactic tells Lean how to construct a new thing from what it already has, and adds it to the stock of resources in the current context. This is what you need to "combine p and pq to get Q".
Since you have pq : P → Q and you have p : P, you can apply pq to p to get a term of Q. This works just like applying a function f : ℕ → ℤ to a term n : ℕ to get a term of ℤ.
So you can continue your proof like this:
example (P Q R : Prop) : (P → Q) ∧ (R → ¬Q) → ¬(P ∧ R) :=
begin
assume a : (P → Q) ∧ (R → ¬Q),
assume b : P ∧ R,
cases a with pq rnq,
cases b with p r,
have q : Q := pq p,
have nq : ¬Q := rnq r,
exact nq q,
end
On the last line, since we now have have q : Q and nq : ¬Q (or equivalently, nq : Q → false), we can apply nq to q to get a term of false. But since that's the goal, we write exact here instead of have.

How to make use of information known about this function type in Coq

Say I have the following type typ representing bool or nat:
Inductive typ : Type := TB | TN.
I also have a function to extract an actual function type from a list of typs and a result type:
Fixpoint get_types (s: seq typ) (result_type: Type) : Type :=
match s with
| nil => result_type
| x :: xs => match x with
| TN => nat -> get_types xs result_type
| TB => bool -> get_types xs result_type
end
end.
Example get_types_works : get_types (TB :: TN :: nil) nat = bool -> nat -> nat.
Proof. by []. Qed.
Now, I have another function that takes as input a list s of typs and a function of type get_types s:
Fixpoint app (s: seq typ) (constructor: get_types s nat) : nat :=
match s with
| nil => 2 (* Not properly handling empty list case for now *)
| TB :: nil => constructor true
| TN :: nil => constructor 2
| TB :: xs => app xs (constructor true)
| TN :: xs => app xs (constructor 2)
end.
Defining the above function fails at the line | TB :: nil => constructor true with:
Illegal application (Non-functional construction):
The expression "constructor" of type "get_types s nat" cannot be applied to the term
"true" : "bool"
Given we know here that the type of get_types s nat should be bool -> nat, as the value of s is TB :: nil, I'm wondering if there's a way we can make Coq aware of this so that the above function can be defined?
If not, is this a limitation of Coq or would the same apply to other dependently typed languages?
Edit: For context, this is not the original problem I'm trying to solve; it's a condensed version to show the issue I was having with the type system. In the actual version, rather than hard-coding 2 and true, the typ-like datastructure also carries indices of data to parse from a memory slice, and validation functions. The aim for app is a function that takes a list of such typs, a slice, and a constructor for a record containing such types, then constructs an instance of that record from the indices of the types to parse, or returns None if any of the validations fail.
There's nothing wrong with what you want in principle. However, at least in Coq, there are some simple rules for how pattern matching is typechecked so that information about which constructor was used can be used in the type. The surface language (Gallina in this case) hides this simplicity by helping compile (or desugar) pattern matches, so that as a user you can write more complex patterns than the underlying system has to deal with. I'm not as familiar with Idris, but based on how complicated dependent pattern matches can be I suspect you run into similar limitations there, where you have to fit your code into a form the system can type check.
Here, you're running into two limitations of this pattern matching compilation. The first is that the type of constructor is not specialized based on the match on s. This is easily fixed by computing a function from get_types s nat -> nat, which the compiler gets right.
Require Import List.
Import ListNotations.
Inductive typ : Type := TB | TN.
Fixpoint get_types (s: list typ) (result_type: Type) : Type :=
match s with
| nil => result_type
| x :: xs => match x with
| TN => nat -> get_types xs result_type
| TB => bool -> get_types xs result_type
end
end.
Fail Fixpoint app (s: list typ) : get_types s nat -> nat :=
match s with
| nil => fun constructor => 2
| TB :: nil => fun constructor => constructor true
| TN :: nil => fun constructor => constructor 2
| TB :: xs => fun constructor => app xs (constructor true)
| TN :: xs => fun constructor => app xs (constructor 2)
end.
(* fails due to limitation of termination checker with nested matches *)
...but then you run into a second problem with the termination checker. Note that your match is complex: it analyzes the structure of s as well as its head and tail (if it was built with cons). Ultimately all pattern matches are compiled to nested pattern matches on a single inductive type. If you look at this unfolding, you're destructing s into t::xs, and then destructing xs again into t0::xs', before finally recursing on xs. Unfortunately, the Coq termination checker only sees this as t0::xs' and doesn't recognize it as a subterm of s (it really wants xs).
I had a hard time manually writing your function in a way that type checks, but here's a version written using tactics that is functionally correct. Note that the definition it produces is quite a bit more complicated than any ordinary pattern match, because it has to maintain a proof produced by destruct_with_eqn. However, that proof is crucial to simultaneously using xs to make the termination checker happy and revealing t0::xs' for type checking the application of the constructor. It may be complicated but you can still run it just fine, as the last line illustrates.
Fixpoint app (s: list typ) (constructor: get_types s nat) {struct s} : nat.
destruct s as [|t xs]; simpl in *.
exact 2.
destruct_with_eqn xs; simpl in *.
destruct t; [ exact (constructor true) | exact (constructor 2) ].
destruct t; simpl in *.
- apply (app xs).
subst.
exact (constructor true).
- apply (app xs).
subst.
exact (constructor 2).
Defined.
Eval compute in app [TB; TN] (fun x y => if x then y+2 else y).
(* = 4
: nat
*)
Because you tagged this with Idris, here is how it works there:
data Typ = TB | TN
get_types : (args : List Typ) -> (res : Type) -> Type
get_types [] res = res
get_types (TB :: xs) res = Bool -> get_types xs res
get_types (TN :: xs) res = Nat -> get_types xs res
app : (args : List Typ) -> (con : get_types args Nat) -> Nat
app [] con = 2
app (TB :: []) con = con True
app (TN :: []) con = con 2
app (TB :: xs) con = app xs (con True)
app (TN :: xs) con = app xs (con 2)
Basically, you don't have the problem, because with matching on args, the compiler also infers the type for con. For example, if you replace the last case with
app (TN :: xs) con = ?hole
and investigate the hole, you see that the compiler has new information about con:
xs : List Typ
con : Nat -> get_types xs Nat
--------------------------------------
hole : Nat
Yet two other ways of defining app.
The first one uses tactics, and relies on induction instead of Fixpoint.
Definition app (s: seq typ) (constructor: get_types s nat) : nat.
Proof.
induction s as [|t xs].
- exact 2.
- destruct xs.
+ destruct t.
* exact (constructor true).
* exact (constructor 2).
+ destruct t.
* exact (IHxs (constructor true)).
* exact (IHxs (constructor 2)).
Defined.
The second one uses Gallina and complexed pattern-matchings.
Fixpoint app (s: seq typ) : get_types s nat -> nat :=
match s return get_types s nat -> nat with
| nil => fun _ => 2
| x :: xs =>
match xs as xs0 return xs = xs0 -> get_types (x::xs0) nat -> nat with
| nil => fun _ => match x return get_types (x::nil) nat -> nat with
| TB => fun c => c true
| TN => fun c => c 2
end
| _ => fun e => match e in _ = xs1 return get_types (x::xs1) nat -> nat with
| eq_refl =>
match x return get_types (x::xs) nat -> nat with
| TB => fun c => app xs (c true)
| TN => fun c => app xs (c 2)
end
end
end eq_refl
end.
When destructing xs, we remember an equality between the original xs and what it is destructed in. We do not need this equality in the nil branch and discards it with fun _. In the other branch, we pattern-match on the proof of equality (match e), which corresponds to a rewriting using this equality. Inside the eq_refl branch, we can use the original xs and thus make recursive calls allowed by the termination checker. Outside the pattern-match, we return the right type expected by the pattern-matching on xs. The last thing to do is to provide a proof of the equality of xs and xs0, but it is trivially eq_refl.
Well, I am not sure what you are really trying to do, but the first step to submit your code into the "convoy" pattern is indeed to add a bit more structure to you type interpretation. If you separate the interpretation of types from the one for list of types you can easily get a skeleton working:
From mathcomp Require Import all_ssreflect.
Set Implicit Arguments.
Unset Strict Implicit.
Unset Printing Implicit Defensive.
Inductive Typ := TB | TN.
(* Interpretation for types *)
Definition iT w : Type :=
match w with | TB => bool | TN => nat end.
(* Default witness *)
Definition dw w : iT w :=
match w with | TB => true | TN => 2 end.
Definition get_types (res : Type) := fix gt (args : list Typ) :=
match args with
| [::] => res
| [:: w & xs] => iT w -> gt xs
end.
Variable (dt : Typ).
Fixpoint app (args : list Typ) : get_types (iT dt) args -> iT dt :=
match args with
| [::] => fun gt => dw dt
| [:: tw & xs] => fun gt => app (gt (dw tw))
end.
Note that I've generalized the return type too as there was no good reason to hardcode the definition to nat. A fun exercise is to modify the above app function and prove it equivalent to the tactic-based version of Tej.

Is it possible to derive induction for the church-encoded Nat?

I was just wondering if it is possible to derive induction for the church-encoded Nat type on Idris, Agda, Coq and similar. Notice this is a different issue from doing it on CoC (which is known to be impossible) because we have much more expressivity on those (we're able to, for example, extract the second element of Sigma).
Here is a poor proof sketch on Idris (had a lot of syntax issues):
CN : Type
CN = (t : Type) -> t -> (t -> t) -> t
CS : CN -> CN
CS n t z s = s (n t z s)
CZ : CN
CZ t z s = z
ind :
(p : CN -> Type) ->
(z : p CZ) ->
(s : (n : CN) -> p n -> p (CS n)) ->
(n : CN) ->
p n
ind p z s n =
let base_case = the (x : CN ** p x) (CZ ** z)
step_case = the ((x : CN ** p x) -> (y : CN ** p y)) (\ (n ** pf) => (CS n ** s n pf))
result = the (x : CN ** p x) (n (x : CN ** p x) base_case step_case)
fst_result = fst result
snd_result = snd result
fst_is_n = the (fst_result = n) ?fst_is_n
in ?wat
I'm doing it by building a Sigma type starting from CZ ** z all way up to CS (CS ... CZ) ** s (s ... z). Problem is that, while I know the first element of it will be equal to n, I'm not sure how to prove it.
Here's a related question I asked about homotopy type theory. I am also a little out my depth here, so take all this with a grain of salt.
I've proved that CN is isomorphic to Nat iff the free theorm for CN holds. Furthermore, it's known that there are no free theorems under the law of excluded middle (in HoTT). I.e. with LEM, you could could define CNs such as
foo : CN
foo T z s = if T is Bool then not z else z
which is not a proper church natural and would not be covered by the induction principle. Because excluded middle and HoTT are consistent with the type theories you are asking about (as far as I know), it follows that there will not be a proof of ind.
It is known not to be provable because there are models of the calculus of constructions where the impredicative encoding of the natural numbers is not initial (i.e. doesn't satisfy induction).
It does follow from relational parametricity as Phil Wadler has shown long time ago. Hence combining Wadler with internal relational parametricity ala Moulin and Bernardy may do the trick.
I think there is no formal proof that it's impossible, but generally expected that it can't be done. See e.g. the introduction to this paper by Aaron Stump.

SML - Why does my code return a weird reference?

I'm trying to write a function that uses references and destructively updates a sorted linked list while inserting a value.
My code is as follows:
Control.Print.printDepth := 100;
datatype 'a rlist = Empty | RCons of 'a * (('a rlist) ref);
fun insert(comp: (('a * 'a) -> bool), toInsert: 'a, lst: (('a rlist) ref)): unit =
let
val r = ref Empty;
fun insert' comp toInsert lst =
case !lst of
Empty => r := (RCons(toInsert, ref Empty))
| RCons(x, L) => if comp(toInsert, x) then r := (RCons(toInsert, lst))
else ((insert(comp,toInsert,L)) ; (r := (RCons(x, L))));
in
insert' comp toInsert lst ; lst := !r
end;
val nodes = ref (RCons(1, ref (RCons(2, ref (RCons(3, ref (RCons(5, ref Empty))))))));
val _ = insert((fn (x,y) => if x <= y then true else false), 4, nodes);
!nodes;
!nodes returns
val it = RCons (1,ref (RCons (2,ref (RCons (3,ref (RCons (4,%1)) as %1)))))
: int rlist
when it should return
val it = RCons (1,ref (RCons (2,ref (RCons (3,ref (RCons (4, ref (RCons(5, ref Empty))))))))
: int rlist
It means that your code is buggy, and has returned a cyclic list, where the tail of ref(RCons(4, ...)) is actually the same ref(RCons(4, ...)) again.
Remarks:
You don't need to pass comp and toInsert to the inner function, they are already in scope.
if C then true else false is the same as writing just C.
In SML, you typically use comparison functions of type t * t -> order, and they are predefined in the library, see e.g. Int.compare.
About 70% of the parentheses in your code are redundant.
You don't normally want to use such a data structure in ML.
If you absolutely have to, here is how I would write the code:
datatype 'a rlist' = Empty | Cons of 'a * 'a rlist
withtype 'a rlist = 'a rlist' ref
fun insert compare x l =
case !l of
Empty => l := Cons(x, ref Empty)
| Cons(y, l') =>
case compare(x, y) of
LESS => l := Cons(x, ref(!l))
| EQUAL => () (* or whatever else you want to do in this case *)
| GREATER => insert compare x l'
Changed
| RCons(x, L) => if comp(toInsert, x) then r := (RCons(toInsert, lst))
to
| RCons(x, L) => if comp(toInsert, x) then r := (RCons(toInsert, ref(!lst)))

Resources