Zulip Chat Archive

Stream: new members

Topic: Induction


Patrick Thomas (Jan 03 2019 at 16:40):

Suppose I have the following:

namespace lambda

inductive exp : Type
| var : string  exp
| app : exp  exp  exp
| abs : string  exp  exp

inductive is_subterm : exp  exp  Prop
-- x ∈ Sub (x)
| var :  (x : string), is_subterm (exp.var x) (exp.var x)

-- e1 ∈ Sub ((e1 e2))
| app_l :  (e1 : exp) (e2 : exp), is_subterm e1 (exp.app e1 e2)

-- e2 ∈ Sub ((e1 e2))
| app_r :  (e1 : exp) (e2 : exp), is_subterm e2 (exp.app e1 e2)

-- (e1 e2) ∈ Sub ((e1 e2))
| app_self :  (e1 : exp) (e2 : exp), is_subterm (exp.app e1 e2) (exp.app e1 e2)

-- e ∈ Sub ((λ x . e))
| abs :  (x : string) (e : exp), is_subterm e (exp.abs x e)

-- (λ x . e) ∈ Sub ((λ x . e))
| abs_self :  (x : string) (e : exp), is_subterm (exp.abs x e) (exp.abs x e)

Is it possible to prove that is_subterm does not hold for something? For example, could one prove that if x is a lambda variable, then the only subterm of x is x itself?

Patrick Massot (Jan 03 2019 at 16:42):

Doesn't it contradict is_subterm.abs?

Patrick Thomas (Jan 03 2019 at 16:46):

Are you saying that the statement "if x is a lambda variable, then the only subterm of x is x itself", does not hold because of is_subterm.abs? I'm not sure I see why that would be the case?

Gabriel Ebner (Jan 03 2019 at 16:46):

This is provable (hint: try the cases tactic):

example (y x) : is_subterm y (exp.var x)  y = exp.var x

Rob Lewis (Jan 03 2019 at 16:46):

You may need to be a little careful about how you state it, but this is a good place to take advantage of the equation compiler. It will discharge the structurally impossible cases for you.

example (s : string) :  (e : exp), is_subterm e (exp.var s)  e = exp.var s
| _ (is_subterm.var _) := rfl

(As Gabriel points out, cases will do the same thing.)

Patrick Massot (Jan 03 2019 at 16:48):

oh I missed "variable" in "lambda variable", it's on the next line here

Patrick Thomas (Jan 03 2019 at 16:52):

Thank you. I will have to do some reading on tactics and the equation compiler.

Patrick Thomas (Jan 03 2019 at 17:09):

I'm still learning how induction is handled in Lean. Does the inductive definition create theorems related to what is not an object of the defined type?

Rob Lewis (Jan 03 2019 at 17:14):

The only objects of the defined type are the ones that can be defined using the constructors. This is a "theorem" that's captured by the type's induction principle. In your case, look at #check is_subterm.cases_on .

Kenny Lau (Jan 03 2019 at 17:37):

should we use nat instead of string?

Patrick Thomas (Jan 03 2019 at 18:03):

Is the following what Gabriel means by the cases tactic?

example (e : exp) (x : string) : is_subterm e (exp.var x)  e = exp.var x :=
    exp.cases_on e
    (show  (y : string), is_subterm (exp.var y) (exp.var x)  (exp.var y) = (exp.var x), from
    assume y : string,

I'm not sure how to proceed from here.

Kenny Lau (Jan 03 2019 at 18:03):

well by the cases tactic he means by cases e

Kenny Lau (Jan 03 2019 at 18:03):

but I don't know if you're familiar with using tactics

Patrick Thomas (Jan 03 2019 at 18:04):

Oh. No, I'm not.

Gabriel Ebner (Jan 03 2019 at 18:06):

Actually I mean by cases on the is_subterm proof:
begin intro h, cases h, end (you can easily solve the remaining goal)

Kenny Lau (Jan 03 2019 at 20:55):

why bother making is_subterm inductive

Patrick Thomas (Jan 03 2019 at 20:57):

I thought it would have to be. What is the alternative?

Kenny Lau (Jan 03 2019 at 21:13):

well it isn't inductive (i.e. recursive) so maybe just make it a def or something

Patrick Thomas (Jan 03 2019 at 21:25):

Hmm. I was trying to formalize the recursive definition of the set of all subterms of a lambda expression given by definition 1.3.5 here: https://play.google.com/books/reader?id=orsrBQAAQBAJ&hl=en_US&pg=GBS.PA5.w.4.0.36
Did I do this wrong?

Rob Lewis (Jan 03 2019 at 21:29):

You're missing the recursive calls. The set of subterms of (M N) is the union of the subterms of M, the subterms of N, and the singleton set {(M N)}.

Rob Lewis (Jan 03 2019 at 21:29):

In your definition, (M N) only has three subterms: M, N, and (M N).

Rob Lewis (Jan 03 2019 at 21:30):

This is a perfectly good situation to use an inductive predicate, but the one you wrote isn't the one you wanted.

Patrick Thomas (Jan 03 2019 at 21:56):

Does adding the following fix it?

| app_l' :  (e : exp) (e1 : exp) (e2 : exp), (is_subterm e e1)  is_subterm e (exp.app e1 e2)
| app_r' :  (e : exp) (e1 : exp) (e2 : exp), (is_subterm e e2)  is_subterm e (exp.app e1 e2)
| abs' :  (e : exp) (x : string) (e1 : exp), (is_subterm e e1)  is_subterm e (exp.abs x e1)

Also, is there a way to formalize this as a set, similar to the definition in the book?

Gabriel Ebner (Jan 03 2019 at 22:00):

Sure, you can just write a recursive function:

def subterms : exp  multiset exp
| (exp.var x) := {exp.var x}
-- ...

I'm not sure why they use multisets instead of sets though.

Rob Lewis (Jan 03 2019 at 22:02):

You could add those, and you can also reduce some of the others into one case.

inductive is_subterm : exp  exp  Prop
| self :  x, is_subterm x x
| app_l :  e₁ e₂ x, is_subterm x e₁  is_subterm x (exp.app e₁ e₂)
| app_r :  e₁ e₂ x, is_subterm x e₂  is_subterm x (exp.app e₁ e₂)
| abs :  e s x, is_subterm x e  is_subterm x (exp.abs s e)

Kenny Lau (Jan 03 2019 at 22:12):

and rename self into refl...

Kenny Lau (Jan 03 2019 at 22:15):

def is_subterm : exp  exp  bool
| e (exp.var s) := e = exp.var s
| e (exp.app e₁ e₂) := (e = exp.app e₁ e₂) || is_subterm e e₁ || is_subterm e e₂
| e (exp.abs s e') := (e = exp.abs s e') || is_subterm e e'

Kenny Lau (Jan 03 2019 at 22:16):

def is_subterm (e : exp) : exp  bool
| (exp.var s) := e = exp.var s
| (exp.app e₁ e₂) := (e = exp.app e₁ e₂) || is_subterm e₁ || is_subterm e₂
| (exp.abs s e') := (e = exp.abs s e') || is_subterm e'

Patrick Thomas (Jan 03 2019 at 22:24):

@Gabriel Ebner Cool. Could something similar be done for the set of all lambda terms?

Patrick Thomas (Mar 08 2019 at 04:26):

I was wondering what the error "nested occurrence 'and (sub_is_def P x N) (not (has_mem.mem.{0 0} var (set.{0} var) (set.has_mem.{0} var) y (FV N)))' contains variables that are not parameters" means in the last definition of:

inductive var : Type

inductive pre_term : Type
| var : var -> pre_term
| app : pre_term -> pre_term -> pre_term
| abs : var -> pre_term -> pre_term

def FV : pre_term -> set var
| ( pre_term.var x ) := { x }
| ( pre_term.app P Q ) := FV ( P )  FV ( Q )
| ( pre_term.abs x P ) := FV ( P ) \ { x }

inductive sub_is_def : pre_term -> var -> pre_term -> Prop
| var_same :
    forall ( y : var ) ( x : var ) ( N : pre_term ),
    ( x = y ) -> sub_is_def ( pre_term.var y ) x N
| var_diff :
    forall ( y : var ) ( x : var ) ( N : pre_term ),
    not ( x = y ) -> sub_is_def ( pre_term.var y ) x N
| app :
    forall ( P : pre_term ) ( Q : pre_term ) ( x : var ) ( N : pre_term ),
    sub_is_def P x N -> sub_is_def Q x N -> sub_is_def ( pre_term.app P Q ) x N
| abs_same :
    forall ( y : var ) ( P : pre_term ) ( x : var ) ( N : pre_term ),
    ( x = y ) -> sub_is_def ( pre_term.abs y P ) x N
| abs_diff :
    forall ( y : var ) ( P : pre_term ) ( x : var ) ( N : pre_term ),
    not ( x = y ) -> x  FV ( pre_term.abs y P )  ( ( sub_is_def P x N )  ( y  FV ( N ) ) ) -> sub_is_def ( pre_term.abs y P ) x N

It seems to be related to the /\ in abs_diff.

Mario Carneiro (Mar 08 2019 at 07:00):

sub_is_def is embedded in a complicated way in the last proposition

Mario Carneiro (Mar 08 2019 at 07:06):

you have to make it a manifestly positive occurrence:

inductive var : Type

inductive pre_term : Type
| var : var  pre_term
| app : pre_term  pre_term  pre_term
| abs : var  pre_term  pre_term

def FV : pre_term  set var
| (pre_term.var x) := {x}
| (pre_term.app P Q) := FV P  FV Q
| (pre_term.abs x P) := FV P \ {x}

inductive sub_is_def : pre_term  var  pre_term  Prop
| var_same (x N) : sub_is_def (pre_term.var x) x N
| var_diff (y x N) : x  y  sub_is_def (pre_term.var y) x N
| app {P Q x N} : sub_is_def P x N  sub_is_def Q x N  sub_is_def (P.app Q) x N
| abs_same (P x N) : sub_is_def (pre_term.abs x P) x N
| abs_diff_nel {x y P N} : x  y  x  FV P  sub_is_def (pre_term.abs y P) x N
| abs_diff {x y P N} : x  y  y  FV N  sub_is_def P x N  sub_is_def (pre_term.abs y P) x N

Patrick Thomas (Mar 09 2019 at 04:22):

Thank you. What does a "manifestly positive occurrence" mean?

Mario Carneiro (Mar 09 2019 at 04:52):

The definition of an inductive specification in lean requires that all constructors for the inductive type T have the form A1 -> A2 -> ... -> T where each A that mentions T has the form B1 -> B2 -> ... T (ignoring indexes). In other words, the only places the type T is allowed to appear is on the far right hand side (the constructor should produce an element of type T), and on the right of the left hand side (the constructors may take arguments of type T or arguments that are functions producing T). This is called "strict positivity". T is not allowed to appear anywhere else, so mk : (T -> A) -> T is not allowed, nor is mk : ((T -> A) -> A) -> T or foo T -> T or A /\ (T \/ B) -> T. The last two cases are called nested inductive types, and lean can sometimes compile them to primitive inductive types, but they aren't built in.

Patrick Thomas (Mar 09 2019 at 05:20):

I think I sort of understand. Thank you.

Patrick Thomas (Mar 09 2019 at 05:50):

Is it possible to have conditions in the constructors for inductively defined functions? For example, how would one define the lambda substitution x [ y := N ], which depends on whether x = y or x != y?

Mario Carneiro (Mar 09 2019 at 05:52):

you can use if

Patrick Thomas (Mar 09 2019 at 05:55):

Is there a description of using if in the doc?

Mario Carneiro (Mar 09 2019 at 05:56):

if p then a else b returns a if p is true and b if false

Mario Carneiro (Mar 09 2019 at 05:57):

In inductive predicates like the above, it's a bit nicer to have separate constructors for the true and false cases, but for recursive functions we usually use if

Patrick Thomas (Mar 09 2019 at 06:13):

For this:

def sub : pre_term  var  pre_term  pre_term
| (pre_term.var y) x N := if (pre_term.var x) = (pre_term.var y) then N else (pre_term.var y)

I get:
failed to synthesize type class instance for
sub : pre_term → var → pre_term → pre_term,
y x : var,
N : pre_term
⊢ decidable (pre_term.var x = pre_term.var y)

Mario Carneiro (Mar 09 2019 at 06:20):

you can say if x = y then ...

Patrick Thomas (Mar 09 2019 at 06:21):

I see. I still get the same error.

Mario Carneiro (Mar 09 2019 at 06:23):

What is var?

Patrick Thomas (Mar 09 2019 at 06:24):

inductive var : Type

Mario Carneiro (Mar 09 2019 at 06:24):

there are no vars?

Patrick Thomas (Mar 09 2019 at 06:24):

I'm not sure what you mean?

Mario Carneiro (Mar 09 2019 at 06:25):

that declaration means var is empty

Mario Carneiro (Mar 09 2019 at 06:25):

you can put @[derive decidable_eq] to automatically derive a decidable instance for var

Patrick Thomas (Mar 09 2019 at 06:26):

Oh. Should I have defined var differently?

Mario Carneiro (Mar 09 2019 at 06:26):

what do you want it to be? One reasonable choice is def var := nat

Patrick Thomas (Mar 09 2019 at 06:28):

I'm not sure. What would make the most sense for the lambda calculus? Maybe a character or a string?

Mario Carneiro (Mar 09 2019 at 06:28):

that works too

Mario Carneiro (Mar 09 2019 at 06:29):

But you have to be careful with how you handle bound variable renaming if you don't use de bruijn indices

Mario Carneiro (Mar 09 2019 at 06:29):

for example, subst can either rename variables, or it can be partial

Mario Carneiro (Mar 09 2019 at 06:31):

having it be a char seems like a bad idea because if the set of variables is finite then weird things happen

Patrick Thomas (Mar 09 2019 at 06:31):

I think it is partial in the book I am following for this. That is, it only defines substitution if it is capture avoiding.

Mario Carneiro (Mar 09 2019 at 06:32):

if your book uses explicit names then string works

Mario Carneiro (Mar 09 2019 at 06:32):

but you will need to prove a renaming lemma at some point, which will require you to know that var is infinite

Patrick Thomas (Mar 09 2019 at 06:33):

Ah. yes.

Patrick Thomas (Mar 09 2019 at 06:39):

I changed to def var := string.
If I have:

def sub : pre_term  var  pre_term  pre_term
| (pre_term.var y) x N := if x = y then N else y

Then I get:
type mismatch at application
ite (x = y) N y
term
y
has type
var
but is expected to have type
pre_term
If I go back to

def sub : pre_term  var  pre_term  pre_term
| (pre_term.var y) x N := if (pre_term.var x) = (pre_term.var y) then N else (pre_term.var y)

Then I still get:
failed to synthesize type class instance for
sub : pre_term → var → pre_term → pre_term,
y x : var,
N : pre_term
⊢ decidable (pre_term.var x = pre_term.var y)

Patrick Thomas (Mar 09 2019 at 06:41):

I see. I still needed @[derive decidable_eq].

Patrick Thomas (Mar 09 2019 at 06:59):

I get the same kind of error for

| (pre_term.app P Q) x N := if sub_is_def P x N  sub_is_def Q x N then ( pre_term.app (sub P x N) (sub Q x N) )

failed to synthesize type class instance for
sub : pre_term → var → pre_term → pre_term,
P Q : pre_term,
x : var,
N : pre_term
⊢ decidable (sub_is_def P x N ∧ sub_is_def Q x N)

Mario Carneiro (Mar 09 2019 at 07:24):

def sub : pre_term  var  pre_term  pre_term
| (pre_term.var y) x N := if x = y then N else y

you are getting a type error because of the y at the end

Mario Carneiro (Mar 09 2019 at 07:28):

| (pre_term.app P Q) x N := if sub_is_def P x N  sub_is_def Q x N then ( pre_term.app (sub P x N) (sub Q x N) )

Here you don't want to have the if at all. You have already defined the domain of definition of sub, so here you can just do the easy thing -

| (pre_term.app P Q) x N := pre_term.app (sub P x N) (sub Q x N)

Mario Carneiro (Mar 09 2019 at 07:29):

Alternatively, you can have sub itself be a (partial) functional relation rather than a recursive function

Mario Carneiro (Mar 09 2019 at 07:29):

that is, define P[x:=N]=QP[x:=N]=Q as an inductive predicate on four arguments

Mario Carneiro (Mar 09 2019 at 07:34):

inductive is_subst : pre_term  var  pre_term  pre_term  Prop
| var_same (x N) : is_subst (pre_term.var x) x N N
| var_diff (y x N) : x  y  is_subst (pre_term.var y) x N (pre_term.var y)
| app {P Q x N P' Q'} : is_subst P x N P'  is_subst Q x N Q'   is_subst (P.app Q) x N (P'.app Q')
| abs_same (P x N) : is_subst (pre_term.abs x P) x N (pre_term.abs x P)
| abs_diff_nel {x y P N} : x  y  x  FV P  is_subst (pre_term.abs y P) x N (pre_term.abs y P)
| abs_diff {x y P N P'} : x  y  y  FV N  is_subst P x N P'  is_subst (pre_term.abs y P) x N (pre_term.abs y P')

Patrick Thomas (Mar 09 2019 at 07:35):

Are you saying that whether P x N and Q x N are defined is already somehow part of the definition of sub?

Mario Carneiro (Mar 09 2019 at 07:36):

I suspect that it will be easier to work with the relation "the substitution of N for x in P is Q" rather than the conjunction of "the substitution is defined" and "the substitution is Q"

Patrick Thomas (Mar 09 2019 at 07:38):

I have been attempting to follow how the book I am going through does it, so that I can prove the theorems in the same manner. I want to improve the readability of the book for myself and others.

Patrick Thomas (Mar 09 2019 at 07:40):

It's a little circular. Reading the book to learn about the theory behind Lean, and using Lean to learn the book.

Patrick Thomas (Mar 09 2019 at 07:42):

In this instance I am formalizing the result of this question: https://math.stackexchange.com/questions/3109334/formal-definition-of-substitution-being-defined-in-type-free-lambda-calculus?noredirect=1&lq=1

Patrick Thomas (Mar 09 2019 at 07:53):

In the case of

| (pre_term.app P Q) x N := pre_term.app (sub P x N) (sub Q x N)

what will happen if I pass a pre_term for P or Q to the function for which the substitution is not defined?

Mario Carneiro (Mar 09 2019 at 07:58):

you get garbage

Mario Carneiro (Mar 09 2019 at 07:59):

but you get garbage no matter what with the "total function" / "domain of definition" approach

Mario Carneiro (Mar 09 2019 at 07:59):

The four place relation avoids this

Patrick Thomas (Mar 09 2019 at 08:05):

I see. I still hate to depart from the book, since many of the proofs involve showing that a given substitution is defined.

Patrick Thomas (Mar 09 2019 at 08:08):

I think using:

def var := string
@[derive decidable_eq]

inductive pre_term : Type
| var : var  pre_term
| app : pre_term  pre_term  pre_term
| abs : var  pre_term  pre_term

def FV : pre_term  set var
| (pre_term.var x) := {x}
| (pre_term.app P Q) := FV P  FV Q
| (pre_term.abs x P) := FV P \ {x}

inductive sub_is_def : pre_term  var  pre_term  Prop
| var_same (x N) : sub_is_def (pre_term.var x) x N
| var_diff (y x N) : x  y  sub_is_def (pre_term.var y) x N
| app {P Q x N} : sub_is_def P x N  sub_is_def Q x N  sub_is_def (P.app Q) x N
| abs_same (P x N) : sub_is_def (pre_term.abs x P) x N
| abs_diff_nel {x y P N} : x  y  x  FV P  sub_is_def (pre_term.abs y P) x N
| abs_diff {x y P N} : x  y  y  FV N  sub_is_def P x N  sub_is_def (pre_term.abs y P) x N

def sub : pre_term  var  pre_term  pre_term
| (pre_term.var y) x N := if x = y then N else (pre_term.var y)
| (pre_term.app P Q) x N := pre_term.app (sub P x N) (sub Q x N)
| (pre_term.abs y P) x N := if x = y then (pre_term.abs y P) else (pre_term.abs y (sub P x N))

may be closest to what they have.

Patrick Thomas (Mar 09 2019 at 08:15):

Although, yes, I guess keeping sub_is_def and adding the four place relation is about the same.

Patrick Thomas (Mar 09 2019 at 18:21):

What does the syntax (P.app Q)mean? Why that and not (pre_term.app P Q)?

Bryan Gin-ge Chen (Mar 09 2019 at 18:26):

I think this is an example of "dot notation". There's a thread on it here: https://leanprover.zulipchat.com/#narrow/stream/113488-general/topic/dot.20notation.20confusion

Patrick Thomas (Mar 09 2019 at 18:53):

Thank you.

Patrick Thomas (Apr 09 2019 at 11:39):

I was wondering if someone would mind showing me how to set up the form of proof for the last lemma here (lemma_1_2_5_i). I'm pretty sure I want to do induction on M, but I'm having difficulty figuring out the syntax.

import data.set
open set

def var := 
@[derive decidable_eq]

inductive pre_term : Type
| var : var  pre_term
| app : pre_term  pre_term  pre_term
| abs : var  pre_term  pre_term

def FV : pre_term  set var
| (pre_term.var x) := {x}
| (pre_term.app P Q) := FV P  FV Q
| (pre_term.abs x P) := FV P \ {x}


lemma lemma_1 {α : Type} {s t : set α} {x : α} : x  s  x  s \ t :=
assume a1 : x  s,
have s1 : x  s \ t  x  s, from set.mem_of_mem_diff,
have s2 : x  s  x  s \ t, from mt s1,
show x  s \ t, from s2 a1


lemma lemma_2 {α : Type} {s t : set α} {x : α} : x  s \ t  x  t  x  s :=
assume a1 : x  s \ t,
assume a2 : x  t,
have s1 : x  s \ t  x  s  x  t, from set.mem_diff x,
have s2 : x  s  x  t  x  s \ t, from s1.elim_left,
have s3 : x  s \ t  ¬ (x  s  x  t), from mt s2,
have s4 : ¬ (x  s  x  t), from s3 a1,
show x  s, from not_and'.elim_left s4 a2


lemma lemma_3 (y : var) (P : pre_term) (x :var ) : x  FV P  x  FV (pre_term.abs y P) :=
assume a1 : x  FV P,
have s1 : x  FV P \ {y}, from lemma_1 a1,
show x  FV (pre_term.abs y P), from s1


lemma lemma_4 (y : var) (P : pre_term) (x : var) : x  FV (pre_term.abs y P)  x  y  x  FV P :=
assume a1 : x  FV (pre_term.abs y P),
assume a2 : x  y,
have s1 : x  FV P \ {y}, from a1,
have s2 : x  {y}, from mt set.mem_singleton_iff.elim_left a2,
show x  FV P, from lemma_2 s1 s2


-- M [ x := N ]
inductive sub_is_def : pre_term  var  pre_term  Prop
| var_same (x N) : sub_is_def (pre_term.var x) x N
| var_diff (y x N) : x  y  sub_is_def (pre_term.var y) x N
| app {P Q x N} : sub_is_def P x N  sub_is_def Q x N  sub_is_def (pre_term.app P Q) x N
| abs_same (P x N) : sub_is_def (pre_term.abs x P) x N
| abs_diff_nel {x y P N} : x  y  x  FV (pre_term.abs y P)  sub_is_def (pre_term.abs y P) x N
| abs_diff {x y P N} : x  y  y  FV N  sub_is_def P x N  sub_is_def (pre_term.abs y P) x N

def sub : pre_term  var  pre_term  pre_term
| (pre_term.var y) x N := if x = y then N else (pre_term.var y)
| (pre_term.app P Q) x N := pre_term.app (sub P x N) (sub Q x N)
| (pre_term.abs y P) x N := if x = y then (pre_term.abs y P) else (pre_term.abs y (sub P x N))

inductive is_subst : pre_term  var  pre_term  pre_term  Prop
| var_same (x N) : is_subst (pre_term.var x) x N N
| var_diff (y x N) : x  y  is_subst (pre_term.var y) x N (pre_term.var y)
| app {P Q x N P' Q'} : is_subst P x N P'  is_subst Q x N Q'  is_subst (pre_term.app P Q) x N (pre_term.app P' Q')
| abs_same (P x N) : is_subst (pre_term.abs x P) x N (pre_term.abs x P)
| abs_diff_nel {x y P N} : x  y  x  FV (pre_term.abs y P)  is_subst (pre_term.abs y P) x N (pre_term.abs y P)
| abs_diff {x y P N P'} : x  y  y  FV N  is_subst P x N P'  is_subst (pre_term.abs y P) x N (pre_term.abs y P')


lemma lemma_1_2_5_i (M : pre_term) (x : var) (N : pre_term) : x  FV M  sub_is_def M x N :=
assume a1 : x  FV M,
show sub_is_def M x N, from sorry

Chris Hughes (Apr 09 2019 at 11:51):

This is one way. Your induction hypothesis will be called lemma_1_2_5_i

lemma lemma_1_2_5_i :  (M : pre_term) (x : var) (N : pre_term), x  FV M  sub_is_def M x N
| (pre_term.var v) := λ x N h, sorry
| (pre_term.app a b) := λ x N h, sorry
| (pre_term.abs a b) := λ x N h, sorry

Mario Carneiro (Apr 09 2019 at 11:54):

Are you familiar with tactic proofs? It's a bit easier to handle these big inductive predicates using induction

Mario Carneiro (Apr 09 2019 at 11:54):

something like this:

lemma lemma_1_2_5_i (M : pre_term) (x : var) (N : pre_term) (a1 : x  FV M) : sub_is_def M x N :=
begin
  induction M,
  case pre_term.var {
    by_cases x = M,
    { subst h, constructor },
    { constructor, assumption } },
  case pre_term.app : M₁ M₂ IH₁ IH₂ {
    constructor,
    { apply IH₁, simp [FV] at a1, tauto },
    { apply IH₂, simp [FV] at a1, tauto } },
  case pre_term.abs : y M IH {
    by_cases x = y,
    { subst h, constructor },
    { exact sub_is_def.abs_diff_nel h a1 } }
end

Patrick Thomas (Apr 09 2019 at 12:02):

Mario: No, not that familiar. I'll have to look these commands up. Thank you.
Chris: What does the h in each case correspond to?

Johan Commelin (Apr 09 2019 at 12:04):

@Patrick Thomas A proof that x ∉ FV M

Patrick Thomas (Apr 09 2019 at 12:05):

Ahh. Thank you.

Patrick Thomas (Apr 09 2019 at 12:46):

How would I match each case of the inductively defined proposition without tactics? For example:

example (M : var) (x : var) (N : pre_term) (h1 : x  FV (pre_term.var M)) : sub_is_def (pre_term.var M) x N := sorry

Patrick Thomas (Apr 09 2019 at 13:01):

Actually, how are each of the cases in sub_is_def matched in the proof that Mario gave?

Mario Carneiro (Apr 09 2019 at 13:17):

The proof is by induction on M, not sub_is_def

Mario Carneiro (Apr 09 2019 at 13:17):

so there is one case for each constructor of pre_term

Mario Carneiro (Apr 09 2019 at 13:18):

But it is proving sub_is_def, and in most of the cases I use constructor to pick the appropriate constructor

Patrick Thomas (Apr 09 2019 at 13:18):

What does constructor do?

Mario Carneiro (Apr 09 2019 at 13:19):

it's basically apply sub_is_def.var_same <|> apply sub_is_def.var_diff <|> ...

Patrick Thomas (Apr 09 2019 at 13:34):

Interesting. Thank you.

Patrick Thomas (Apr 11 2019 at 03:47):

Does the use of the by_cases tactic mean that the proof requires classical reasoning?

Fabian Glöckle (Apr 12 2019 at 13:32):

The by_casestactic needs that the proposition is decidable, and tries to create such an instance by typeclass inference. Decidability can be proven within a theory (like the decidable equality of natin data/nat/basic.lean: instance : decidable_eq ℕ) or be assumed as a classical axiom. This is the line

local attribute [instance] classical.prop_decidable

you find in certain files.

Patrick Thomas (Apr 21 2019 at 01:19):

How is a function with if...then...else used? For example, how would one prove the example in the following:

def var := 

inductive pre_term : Type
| var : var  pre_term
| app : pre_term  pre_term  pre_term
| abs : var  pre_term  pre_term

def sub : pre_term  var  pre_term  pre_term
| (pre_term.var y) x N := if x = y then N else (pre_term.var y)
| (pre_term.app P Q) x N := pre_term.app (sub P x N) (sub Q x N)
| (pre_term.abs y P) x N := if x = y then (pre_term.abs y P) else (pre_term.abs y (sub P x N))

variables x y : var
variable N : pre_term

example := x = y  sub (pre_term.var y) x N = N

Simon Hudon (Apr 21 2019 at 01:20):

split_ifs is your friend when you see ite or dite (the functions underneath the if _ then _ else _) in your goal

Patrick Thomas (Apr 21 2019 at 01:26):

I don't think I see ite or dite? Maybe the goal hasn't been broken down enough yet?

Simon Hudon (Apr 21 2019 at 01:29):

First unfold sub, then you'll see it

Patrick Thomas (Apr 21 2019 at 01:36):

I'm sorry, I'm new to tactics. Would you mind giving an example? I'd be curious how to use sub without tactics as well.

Mario Carneiro (Apr 21 2019 at 01:38):

without tactics:

example (h : x = y) : sub (pre_term.var y) x N = N := if_pos h

Mario Carneiro (Apr 21 2019 at 01:39):

with tactics:

example (h : x = y) : sub (pre_term.var y) x N = N :=
by {unfold sub, simp [h]}

Patrick Thomas (Apr 21 2019 at 01:43):

Thank you!

Patrick Thomas (Apr 27 2019 at 21:13):

How does the if_pos example know which constructor of sub to use? Can it be explicitly told?

Patrick Thomas (Apr 27 2019 at 21:15):

When I clicked on the Induction title it showed two threads, one named INduction at the top and the other named Induction after.

Kevin Buzzard (Apr 27 2019 at 21:33):

What's the if_pos example?

Kevin Buzzard (Apr 27 2019 at 22:05):

I don't understand your question.

Kevin Buzzard (Apr 27 2019 at 22:06):

You know you can hover on terms in VS Code to see their type? Does this sort of technique answer your question?

Kevin Buzzard (Apr 27 2019 at 22:07):

example : sub (pre_term.var y) x N = if x = y then N else (pre_term.var y) := rfl

Does that answer your question? The left hand side equals the if statement by definition.

Patrick Thomas (Apr 28 2019 at 17:40):

I guess I mean: What is being substituted for what in the definition of if_pos and how does it decide on those particular substitutions? Is there a way in Visual Studio to see what substitutions are being made?

Alistair Tucker (Apr 28 2019 at 19:24):

You mean the implicit arguments to if_pos? The stuff in curly brackets?

Alistair Tucker (Apr 28 2019 at 19:26):

example (h : x = y) : sub (pre_term.var y) x N = N := @if_pos (x = y) _ h pre_term N (pre_term.var y)

Mario Carneiro (Apr 28 2019 at 19:29):

How does the if_pos example know which constructor of sub to use? Can it be explicitly told?

sub doesn't have constructors - it's a recursive definition. So sub (pre_term.var y) x N and if x = y then N else (pre_term.var y) are the same thing from lean's point of view, there is no constructor application to turn one into the other, just definitional unfolding

Kevin Buzzard (Apr 28 2019 at 19:51):

example (h : x = y) : sub (pre_term.var y) x N = N := if_pos h

#check if_pos
/-
if_pos : ?M_1 → ∀ {α : Sort u_1} {t e : α}, ite ?M_1 t e = t
-/
#check @if_pos -- sometimes output is clearer
/-
if_pos : ∀ {c : Prop} [h : decidable c],
  c → ∀ {α : Sort u_1} {t e : α}, ite c t e = t
-/

example (h : x = y) : sub (pre_term.var y) x N = N := @if_pos _ _ h _ _ _

Kevin Buzzard (Apr 28 2019 at 19:52):

Of the five inputs to if_pos that you didn't give, four were worked out by just thinking about the types of things, and the value of h : decidable c was supplied by the type class inference system.

Kevin Buzzard (Apr 28 2019 at 19:53):

example (h : x = y) : sub (pre_term.var y) x N = N := @if_pos _ (by apply_instance) h _ _ _

Patrick Thomas (May 03 2019 at 18:45):

I think I understand now. Thank you!

Patrick Thomas (May 06 2019 at 20:33):

I was wondering if someone might be able to show me how to use the equalities sub P x N = P and sub Q x N = Q to show that pre_term.app (sub P x N) (sub Q x N) = pre_term.app P Q given

inductive pre_term : Type
| var : var  pre_term
| app : pre_term  pre_term  pre_term
| abs : var  pre_term  pre_term

def sub : pre_term  var  pre_term  pre_term
-- if x = y then y [ x := N ] = N else y [ x := N ] = y
| (pre_term.var y) x N := if (x = y) then N else (pre_term.var y)

-- (P Q) [ x := N ] = (P [ x := N ] Q [ x := N ])
| (pre_term.app P Q) x N := pre_term.app (sub P x N) (sub Q x N)

-- if x = y then ( λ y . P ) [ x := N ] = ( λ y . P ) else ( λ y . P ) [ x := N ] = ( λ y . P [ x := N ] )
| (pre_term.abs y P) x N := if x = y then (pre_term.abs y P) else (pre_term.abs y (sub P x N))

Thank you.

Kevin Buzzard (May 06 2019 at 20:34):

Can you post fully working code? It makes everyone's life easier.

Patrick Thomas (May 06 2019 at 20:34):

Sure, give me a second.

Kevin Buzzard (May 06 2019 at 20:35):

Can you formalise precisely what you want proved as well, rather than just describing it?

Patrick Thomas (May 06 2019 at 20:49):

I think this is a minimum working example:

def var := 

inductive pre_term : Type
| var : var  pre_term
| app : pre_term  pre_term  pre_term
| abs : var  pre_term  pre_term

def sub : pre_term  var  pre_term  pre_term
-- if x = y then y [ x := N ] = N else y [ x := N ] = y
| (pre_term.var y) x N := if (x = y) then N else (pre_term.var y)

-- (P Q) [ x := N ] = (P [ x := N ] Q [ x := N ])
| (pre_term.app P Q) x N := pre_term.app (sub P x N) (sub Q x N)

-- if x = y then ( λ y . P ) [ x := N ] = ( λ y . P ) else ( λ y . P ) [ x := N ] = ( λ y . P [ x := N ] )
| (pre_term.abs y P) x N := if x = y then (pre_term.abs y P) else (pre_term.abs y (sub P x N))


example (P Q N : pre_term) (x : var) (h1 : sub P x N = P) (h2 : sub Q x N = Q) :
    pre_term.app (sub P x N) (sub Q x N) = pre_term.app P Q := sorry

Kevin Buzzard (May 06 2019 at 20:59):

example (P Q N : pre_term) (x : var) (h1 : sub P x N = P) (h2 : sub Q x N = Q) :
    pre_term.app (sub P x N) (sub Q x N) = pre_term.app P Q :=
begin
  rw h1,
  rw h2,
end

Patrick Thomas (May 06 2019 at 21:01):

Thank you. Do you mind showing how it might be done without tactics?

Kevin Buzzard (May 06 2019 at 21:01):

#print the proof term? :-/

Kevin Buzzard (May 06 2019 at 21:01):

or you could try the dreaded triangle

Patrick Thomas (May 06 2019 at 21:01):

Triangle?

Kevin Buzzard (May 06 2019 at 21:02):

\t

Kevin Buzzard (May 06 2019 at 21:02):

eq.subst

Patrick Massot (May 06 2019 at 21:02):

substituting in what?

Kevin Buzzard (May 06 2019 at 21:02):

it's a version of rw which works in term mode but never works for me.

Patrick Massot (May 06 2019 at 21:02):

in (rlf : pre_term.app P Q = pre_term.app P Q) maybe

Kevin Buzzard (May 06 2019 at 21:02):

substituting in what?

I dunno, I can never get it to work

Patrick Massot (May 06 2019 at 21:03):

Anyway, let's not encourage Patrick's masochism here

Kevin Buzzard (May 06 2019 at 21:04):

it won't work, it will change too many P's

Patrick Massot (May 06 2019 at 21:04):

@Patrick Thomas you really need to learn tactic mode

Patrick Thomas (May 06 2019 at 21:05):

I think it is more that I like it to be explicit. I guess in this case it is obvious enough.

Kevin Buzzard (May 06 2019 at 21:05):

def ABC (P Q N : pre_term) (x : var) (h1 : sub P x N = P) (h2 : sub Q x N = Q) :
    pre_term.app (sub P x N) (sub Q x N) = pre_term.app P Q :=
begin
  rw h1,
  rw h2,
end

#print ABC
/-
def ABC : ∀ (P Q N : pre_term) (x : var),
  sub P x N = P → sub Q x N = Q → pre_term.app (sub P x N) (sub Q x N) = pre_term.app P Q :=
λ (P Q N : pre_term) (x : var) (h1 : sub P x N = P) (h2 : sub Q x N = Q),
  eq.mpr (id (eq.rec (eq.refl (pre_term.app (sub P x N) (sub Q x N) = pre_term.app P Q)) h1))
    (eq.mpr (id (eq.rec (eq.refl (pre_term.app P (sub Q x N) = pre_term.app P Q)) h2)) (eq.refl (pre_term.app P Q)))
    -/

Patrick Massot (May 06 2019 at 21:05):

How nicely explicit!

Patrick Thomas (May 06 2019 at 21:06):

LOL

Patrick Massot (May 06 2019 at 21:06):

Maybe you can remove a couple of id and still be explicit enough

Kevin Buzzard (May 06 2019 at 21:07):

Take a look at mathlib. They love obfuscated one-line term mode proofs -- when they can get them to work. But there are plenty of times in mathlib when they go into tactic mode precisely for reasons such as this. In mathlib the proof would be by rw [h1, h2]. rw is a powerful tactic. Why try emulating it in term mode?

Patrick Thomas (May 06 2019 at 21:07):

Yeah, I think in this case I'll use the tactic.

Patrick Massot (May 06 2019 at 21:08):

Yes rw is the tactic mode reason number one in mathlib

Kevin Buzzard (May 06 2019 at 21:08):

Many of my tactic mode proofs are rw this, change that, rw this, change that, rwa this

Kevin Buzzard (May 06 2019 at 21:08):

As Patrick says, it's the number one reason to go into tactic mode.

Kevin Buzzard (May 06 2019 at 21:09):

At least for me. If I could use simp better then this might not be the case -- I am slowly learning how to use it.

Kevin Buzzard (May 06 2019 at 21:13):

def ABC (P Q N : pre_term) (x : var) (h1 : sub P x N = P) (h2 : sub Q x N = Q) :
    pre_term.app (sub P x N) (sub Q x N) = pre_term.app P Q :=
have H : pre_term.app (sub P x N) Q = pre_term.app P Q, from h1.symm  rfl,
h2.symm  H

Triangle level up

Kevin Buzzard (May 06 2019 at 21:14):

def ABC (P Q N : pre_term) (x : var) (h1 : sub P x N = P) (h2 : sub Q x N = Q) :
    pre_term.app (sub P x N) (sub Q x N) = pre_term.app P Q :=
h2.symm  (h1.symm  rfl)

Triangle master!

Patrick Massot (May 06 2019 at 21:14):

Hey, that was my proposal!

Patrick Massot (May 06 2019 at 21:14):

Patrick, do you really think this is more explicit than rw?

Kevin Buzzard (May 06 2019 at 21:14):

The symms are for some reason crucial

Patrick Massot (May 06 2019 at 21:14):

Ahh

Kevin Buzzard (May 06 2019 at 21:14):

and the rfl figured out its own type, to my surprise

Patrick Massot (May 06 2019 at 21:15):

I didn't try

Kevin Buzzard (May 06 2019 at 21:15):

but honestly, triangle does not scale :-/

Kevin Buzzard (May 06 2019 at 21:17):

Seems that the triangle by default picks up the first match shrug

Patrick Thomas (May 06 2019 at 21:21):

Patrick: I think the rewrite works well here. I haven't used tactics enough for it to have occurred to me.

Andrew Ashworth (May 06 2019 at 21:53):

If you want to get rid of the symms, you could try eq.substr

Andrew Ashworth (May 06 2019 at 21:53):

unfortunately it doesn't have the nice triangle notation like eq.subst does

Patrick Thomas (May 11 2019 at 21:57):

Given:

def var := 

inductive pre_term : Type
| var : var  pre_term
| app : pre_term  pre_term  pre_term
| abs : var  pre_term  pre_term

def FV : pre_term  set var
| (pre_term.var x) := {x}
| (pre_term.app P Q) := (FV P)  (FV Q)
| (pre_term.abs x P) := (FV P) \ {x}


-- sub_is_def M x N means M [ x := N ] is defined
inductive sub_is_def : pre_term  var  pre_term  Prop

-- y [ x := N ] is defined
| var (y : var) (x : var) (N : pre_term) :
  sub_is_def (pre_term.var y) x N

-- P [ x := N ] is defined → Q [ x := N ] is defined → (P Q) [ x := N ] is defined
| app (P : pre_term) (Q : pre_term) (x : var) (N : pre_term) :
  sub_is_def P x N  sub_is_def Q x N  sub_is_def (pre_term.app P Q) x N

-- x = y → ( λ y . P ) [ x := N ] is defined
| abs_same (y : var) (P : pre_term) (x : var) (N : pre_term) :
  x = y  sub_is_def (pre_term.abs y P) x N

-- x ≠ y → x ∉ FV ( λ y . P ) → ( λ y . P ) [ x := N ] is defined
| abs_diff_nel (y : var) (P : pre_term) (x : var) (N : pre_term) :
  x  y  x  FV (pre_term.abs y P)  sub_is_def (pre_term.abs y P) x N

-- x ≠ y → y ∉ FV ( N ) → P [ x := N ] is defined → ( λ y . P ) [ x := N ] is defined
| abs_diff (y : var) (P : pre_term) (x : var) (N : pre_term) :
  x  y  y  FV N  sub_is_def P x N  sub_is_def (pre_term.abs y P) x N


-- M [ x := N ]
def sub : pre_term  var  pre_term  pre_term
-- if x = y then y [ x := N ] = N else y [ x := N ] = y
| (pre_term.var y) x N := if (x = y) then N else (pre_term.var y)

-- (P Q) [ x := N ] = (P [ x := N ] Q [ x := N ])
| (pre_term.app P Q) x N := pre_term.app (sub P x N) (sub Q x N)

-- if x = y then ( λ y . P ) [ x := N ] = ( λ y . P ) else ( λ y . P ) [ x := N ] = ( λ y . P [ x := N ] )
| (pre_term.abs y P) x N := if x = y then (pre_term.abs y P) else (pre_term.abs y (sub P x N))

Is the only way to combine sub_is_def and sub to use:

-- is_sub M x N L means M [ x := N ] = L
inductive is_sub : pre_term  var  pre_term  pre_term  Prop

-- x = y → y [ x := N ] = N
| var_same (y : var) (x : var) (N : pre_term) :
  x = y  is_sub (pre_term.var y) x N N

-- x ≠ y → y [ x := N ] = y
| var_diff (y : var) (x : var) (N : pre_term) :
  x  y  is_sub (pre_term.var y) x N (pre_term.var y)

-- P [ x := N ] = P' → Q [ x := N ] = Q' → (P Q) [ x := N ] = (P' Q')
| app (P : pre_term) (Q : pre_term) (x : var) (N : pre_term) (P' : pre_term) (Q' : pre_term) :
  is_sub P x N P'  is_sub Q x N Q'  is_sub (pre_term.app P Q) x N (pre_term.app P' Q')

-- x = y → ( λ y . P ) [ x := N ] = ( λ y . P )
| abs_same (y : var) (P : pre_term) (x : var) (N : pre_term) :
  x = y  is_sub (pre_term.abs y P) x N (pre_term.abs y P)

-- x ≠ y → x ∉ FV ( λ y . P ) → ( λ y . P ) [ x := N ] = ( λ y . P )
| abs_diff_nel (y : var) (P : pre_term) (x : var) (N : pre_term) :
  x  y  x  FV (pre_term.abs y P)  is_sub (pre_term.abs y P) x N (pre_term.abs y P)

-- x ≠ y → y ∉ FV ( N ) → P [ x := N ] = P' → ( λ y . P ) [ x := N ] = ( λ y . P' )
| abs_diff (y : var) (P : pre_term) (x : var) (N : pre_term) (P' : pre_term) :
  x  y  y  FV N  is_sub P x N P'  is_sub (pre_term.abs y P) x N (pre_term.abs y P')

I can not make sub a function only on the domain for which sub_is_def holds?

Mario Carneiro (May 12 2019 at 07:01):

You can make sub (M : pre_term) (x : var) (N : pre_term) : sub_is_def M x N -> pre_term, but I strongly recommend against it

Patrick Thomas (May 12 2019 at 16:29):

Why the recommendation against it?

Patrick Thomas (May 27 2019 at 20:59):

Should I be able to prove the following?

lemma sub_iff (x :var) (L M N : pre_term) : ( is_sub M x N L )  ( M [ x := N ] is_def )  ( M [ x := N ] = L )

Perhaps by induction on M?

Mario Carneiro (May 27 2019 at 20:59):

what does is_def mean in the first conjunct?

Patrick Thomas (May 27 2019 at 21:01):

Sorry, left out the notation:

notation M `[` x `:=` N `]` `is_def` := sub_is_def M x N
notation M `[` x `:=` N `]` := sub M x N

Mario Carneiro (May 27 2019 at 21:01):

wow that's an ambiguous notation

Kevin Buzzard (May 27 2019 at 21:01):

Why not just post your full working code?

Mario Carneiro (May 27 2019 at 21:02):

it's mostly the same as the code in this thread I think

Patrick Thomas (May 27 2019 at 21:03):

Yeah. Here it is

import data.set
open set


def var := 

inductive pre_term : Type
| var : var  pre_term
| app : pre_term  pre_term  pre_term
| abs : var  pre_term  pre_term


def FV : pre_term  set var
| (pre_term.var x) := {x}
| (pre_term.app P Q) := (FV P)  (FV Q)
| (pre_term.abs x P) := (FV P) \ {x}


-- sub_is_def M x N means M [ x := N ] is defined
inductive sub_is_def : pre_term  var  pre_term  Prop

-- y [ x := N ] is defined
| var (y : var) (x : var) (N : pre_term) :
  sub_is_def (pre_term.var y) x N

-- P [ x := N ] is defined → Q [ x := N ] is defined → (P Q) [ x := N ] is defined
| app (P : pre_term) (Q : pre_term) (x : var) (N : pre_term) :
  sub_is_def P x N  sub_is_def Q x N  sub_is_def (pre_term.app P Q) x N

-- x = y → ( λ y . P ) [ x := N ] is defined
| abs_same (y : var) (P : pre_term) (x : var) (N : pre_term) :
  x = y  sub_is_def (pre_term.abs y P) x N

-- x ≠ y → x ∉ FV ( λ y . P ) → ( λ y . P ) [ x := N ] is defined
| abs_diff_nel (y : var) (P : pre_term) (x : var) (N : pre_term) :
  x  y  x  FV (pre_term.abs y P)  sub_is_def (pre_term.abs y P) x N

-- x ≠ y → y ∉ FV ( N ) → P [ x := N ] is defined → ( λ y . P ) [ x := N ] is defined
| abs_diff (y : var) (P : pre_term) (x : var) (N : pre_term) :
  x  y  y  FV N  sub_is_def P x N  sub_is_def (pre_term.abs y P) x N

notation M `[` x `:=` N `]` `is_def` := sub_is_def M x N


-- M [ x := N ]
def sub : pre_term  var  pre_term  pre_term
-- if x = y then y [ x := N ] = N else y [ x := N ] = y
| (pre_term.var y) x N := if (x = y) then N else (pre_term.var y)

-- (P Q) [ x := N ] = (P [ x := N ] Q [ x := N ])
| (pre_term.app P Q) x N := pre_term.app (sub P x N) (sub Q x N)

-- if x = y then ( λ y . P ) [ x := N ] = ( λ y . P ) else ( λ y . P ) [ x := N ] = ( λ y . P [ x := N ] )
| (pre_term.abs y P) x N := if x = y then (pre_term.abs y P) else (pre_term.abs y (sub P x N))

notation M `[` x `:=` N `]` := sub M x N


-- is_sub M x N L means M [ x := N ] = L
inductive is_sub : pre_term  var  pre_term  pre_term  Prop

-- x = y → y [ x := N ] = N
| var_same (y : var) (x : var) (N : pre_term) :
  x = y  is_sub (pre_term.var y) x N N

-- x ≠ y → y [ x := N ] = y
| var_diff (y : var) (x : var) (N : pre_term) :
  x  y  is_sub (pre_term.var y) x N (pre_term.var y)

-- P [ x := N ] = P' → Q [ x := N ] = Q' → (P Q) [ x := N ] = (P' Q')
| app (P : pre_term) (Q : pre_term) (x : var) (N : pre_term) (P' : pre_term) (Q' : pre_term) :
  is_sub P x N P'  is_sub Q x N Q'  is_sub (pre_term.app P Q) x N (pre_term.app P' Q')

-- x = y → ( λ y . P ) [ x := N ] = ( λ y . P )
| abs_same (y : var) (P : pre_term) (x : var) (N : pre_term) :
  x = y  is_sub (pre_term.abs y P) x N (pre_term.abs y P)

-- x ≠ y → x ∉ FV ( λ y . P ) → ( λ y . P ) [ x := N ] = ( λ y . P )
| abs_diff_nel (y : var) (P : pre_term) (x : var) (N : pre_term) :
  x  y  x  FV (pre_term.abs y P)  is_sub (pre_term.abs y P) x N (pre_term.abs y P)

-- x ≠ y → y ∉ FV ( N ) → P [ x := N ] = P' → ( λ y . P ) [ x := N ] = ( λ y . P' )
| abs_diff (y : var) (P : pre_term) (x : var) (N : pre_term) (P' : pre_term) :
  x  y  y  FV N  is_sub P x N P'  is_sub (pre_term.abs y P) x N (pre_term.abs y P')


lemma sub_iff (x :var) (L M N : pre_term) : ( is_sub M x N L )  ( M [ x := N ] is_def )  ( M [ x := N ] = L ) := sorry

Mario Carneiro (May 27 2019 at 21:05):

seems like this is the wrong question. The answer to "is this provable" is either "no you got the math wrong" or "yes this can be formalized"

Kenny Lau (May 27 2019 at 21:07):

what if the provability is independent of the Lean axioms? :P

Patrick Thomas (May 27 2019 at 21:07):

That would be good to know too :)

Mario Carneiro (May 27 2019 at 21:07):

do you bring that up in all your intro classes Kenny?

Kevin Buzzard (May 27 2019 at 21:08):

In maths it's very unlikely that something Kenny saw so far would be even close to being indepedent!

Kevin Buzzard (May 27 2019 at 21:08):

Maybe I mentioned CH but I bet that's it.

Mario Carneiro (May 27 2019 at 21:08):

well, as you know there are several independent statements "close to the surface" in lean because MLTT is underdetermined... but this is off topic

Patrick Thomas (May 27 2019 at 21:10):

I guess my question is, that it was suggested that I use is_sub instead of the other two, and I was wondering if that is equivalent. I thought maybe this lemma would show that it is, if it is true.

Mario Carneiro (May 27 2019 at 21:12):

it is true, unless there is some subtle bug in the inductives

Patrick Thomas (May 27 2019 at 21:13):

Ok, thank you. I will try to prove it. Probably induction on M?

Mario Carneiro (May 27 2019 at 21:14):

induction on the predicates

Patrick Thomas (May 27 2019 at 21:15):

Hmm, I'm not sure I am familiar with that form of induction.

Patrick Thomas (May 27 2019 at 21:42):

How would you set that up? Can you use the induction tactic?

Kevin Buzzard (May 27 2019 at 21:59):

Yes. If you have some hypothesis h whose type is an inductive type, then induction h will do some kind of induction on h.

Patrick Thomas (May 27 2019 at 22:04):

I guess that is not the case here? Mario suggested induction on the predicates. Maybe that means is_sub.rec_on?

Kevin Buzzard (May 27 2019 at 23:00):

If you are in tactic mode and have h : is_sub (pre_term.var M) x N L then cases h or induction h (or, better, cases h with AAA BBB CCC DDD EEE FFF GGG and then look at the types of what you ended up with and rename accordingly) should work just fine. Yes, they're just applying rec under the hood.

Patrick Thomas (May 27 2019 at 23:11):

Thank you!

Patrick Thomas (May 28 2019 at 00:46):

Should there be induction hypotheses, for instance for the is_sub.app case?

Patrick Thomas (May 28 2019 at 00:47):

I don't see any under the goal.

Patrick Thomas (May 28 2019 at 02:37):

Do I need to add an axiom to have ( is_sub M x N P ∧ is_sub M x N Q ) → P = Q?

Kevin Buzzard (May 28 2019 at 08:15):

You shouldn't ever have to add axioms.

Patrick Thomas (May 28 2019 at 20:43):

Do you know how I might be able to show this?

Mario Carneiro (May 28 2019 at 20:45):

induction on the first is_sub (generalizing Q), cases on the other

Patrick Thomas (May 28 2019 at 21:00):

Thank you. On the proof of the earlier correspondence between is_sub and sub, sub_is_def, when I use cases, I kind of expected induction hypotheses in the goals, but I don't see any. Am I missing something in my understanding of the proof, or in the use of cases?

Mario Carneiro (May 28 2019 at 21:30):

cases is like induction except there are no induction hypotheses

Mario Carneiro (May 28 2019 at 21:30):

if you need an IH (and you will in these proofs) use induction

Patrick Thomas (May 28 2019 at 21:35):

I see. That makes sense. Feeling kind of silly. Thank you.

Patrick Thomas (Jun 06 2019 at 01:42):

Nevermind.

Mario Carneiro (Jun 06 2019 at 01:57):

I fixed your formatting

import data.set.basic
open set
local attribute [instance, priority 0] classical.prop_decidable

def var := 

inductive pre_term : Type
| var : var  pre_term
| app : pre_term  pre_term  pre_term
| abs : var  pre_term  pre_term

def FV : pre_term  set var
| (pre_term.var x) := {x}
| (pre_term.app P Q) := FV P  FV Q
| (pre_term.abs x P) := FV P \ {x}

lemma lemma_1 {x : var} {P Q : pre_term} : x  FV (pre_term.app P Q)  x  FV P  x  FV Q :=
have s1 : FV (pre_term.app P Q) = FV P  FV Q, by refl,
have s2 : x  FV (pre_term.app P Q)  x  FV P  FV Q, from (ext_iff (FV (pre_term.app P Q)) (FV P  FV Q)).elim_left s1 x,
have s3 : x  FV P  FV Q  ((x  FV P)  (x  FV Q)), from mem_union x (FV P) (FV Q),
iff_iff_eq.elim_right (eq.trans (iff_iff_eq.elim_left s2) (iff_iff_eq.elim_left s3))

lemma lemma_2 {x : var} {y : var} {P : pre_term} : x  FV (pre_term.abs y P)  x  FV P  x  y :=
begin
have : x  FV (pre_term.abs y P)  (x  FV P  x  { y }), from mem_diff x,
have : x  { y }  x  y, from not_iff_not.elim_right mem_singleton_iff,
simp *
end


-- M [ x := N ]
def sub : pre_term  var  pre_term  pre_term
-- if x = y then y [ x := N ] = N else y [ x := N ] = y
| (pre_term.var y) x N := if (x = y) then N else (pre_term.var y)

-- (P Q) [ x := N ] = (P [ x := N ] Q [ x := N ])
| (pre_term.app P Q) x N := pre_term.app (sub P x N) (sub Q x N)

-- if x = y then (λ y . P) [ x := N ] = (λ y . P) else (λ y . P) [ x := N ] = (λ y . P [ x := N ])
| (pre_term.abs y P) x N := if x = y then (pre_term.abs y P) else (pre_term.abs y (sub P x N))


lemma lemma_3 {α : Type} {s t : set α} {x : α} : s = t  x  s  x  t :=
assume a1 : s = t,
assume a2 : x  s,
show x  t, from ((ext_iff s t).elim_left a1 x).elim_left a2

lemma lemma_1_2_5_ii {M : pre_term} {x : var} {N : pre_term} {z : var} :
z  FV (sub M x N)  (z  FV M  x  z)  (z  FV N  x  FV M) :=
begin
  split,
  { intro a1,
    show (z  FV M  x  z)  (z  FV N  x  FV M), sorry },
  { assume a1 : (z  FV M  x  z)  (z  FV N  x  FV M),
    show z  FV (sub M x N),
    induction M,
    case pre_term.var {
      sorry },
    case pre_term.app : P Q IH_1 IH_2 {
      sorry },
    case pre_term.abs : y P IH {
      have s1 : (z  FV (pre_term.abs y P)  x  z)  (z  FV N  x  FV (pre_term.abs y P)), from a1,
      refine or.elim s1 sorry _,
      assume a2 : z  FV N  x  FV (pre_term.abs y P),
      have s2 : z  FV N := and.elim_left a2,
      have s3 : x  FV (pre_term.abs y P) := and.elim_right a2,
      have s4 : x  FV P  x  y := lemma_2.elim_left s3,
      have s5 : x  FV P := and.elim_left s4,
      have s6 : z  FV N  x  FV P := and.intro s2 s5,
      have s7 : (z  FV P  x  z)  (z  FV N  x  FV P)  z  FV (sub P x N) := IH,
      have s8 : z  FV (sub P x N) := s7 (or.inr s6),
      have s9 : x  y := and.elim_right s4,
      have s5 : sub (pre_term.abs y P) x N = (pre_term.abs y (sub P x N)) := if_neg s9,
      show z  FV (sub (pre_term.abs y P) x N),
      sorry } }
end

Patrick Thomas (Jun 06 2019 at 02:22):

The lemma won't work though right? Because I left out the hypothesis that M [ x := N ] is defined? Which is why I should use the is_sub definition?

Mario Carneiro (Jun 06 2019 at 02:22):

Here's my attempt at this lemma and the others leading up to it:

lemma lemma_1 {x : var} {P Q : pre_term} : x  FV (pre_term.app P Q)  x  FV P  x  FV Q :=
mem_union _ _ _

lemma lemma_2 {x : var} {y : var} {P : pre_term} : x  FV (pre_term.abs y P)  x  FV P  x  y :=
by simp [FV]

lemma lemma_3 {α : Type} {s t : set α} {x : α} (h : s = t) (hx : x  s) : x  t :=
h  hx

lemma lemma_1_2_5_ii {M : pre_term} {x : var} {N : pre_term} {z : var} :
z  FV (sub M x N)  (z  FV M  x  z)  (z  FV N  x  FV M) :=
begin
  induction M with y P Q IH_1 IH_2 y P IH; simp [sub, FV],
  { split_ifs,
    { subst x, simp [eq_comm] },
    { simp [h, FV], rw and_iff_left_of_imp,
      intro, subst z, exact h } },
  { simp [IH_1, IH_2, and_or_distrib_left,
      or_and_distrib_right, or.left_comm, or_assoc] },
  { split_ifs,
    { subst x, simp [eq_comm, and_assoc, FV] },
    { simp [FV, IH, h],
      by_cases zy : z = y,
      { subst z, simp, sorry },
      { simp [zy] } } }
end

Mario Carneiro (Jun 06 2019 at 02:23):

something goes wrong in the x != y, z = y case of abs

Patrick Thomas (Jun 06 2019 at 02:24):

Right. Because I left out the hypothesis that M [ x := N ] is defined? Which is why I should have used the is_sub definition?

Mario Carneiro (Jun 06 2019 at 02:45):

yes, that does the trick.

lemma sub_eq_self {M x N} (H : x  FV M) : sub M x N = M :=
begin
  induction M with y P Q IH_1 IH_2 y P IH; simp [sub, FV] at H ,
  { simp [H] },
  { simp [not_or_distrib] at H, exact H.imp IH_1 IH_2 },
  { split_ifs,
    { simp },
    { exact rfl, IH (mt H h) } }
end

lemma lemma_1_2_5_ii {M x N z} (H : sub_is_def M x N) :
  z  FV (sub M x N)  (z  FV M  x  z)  (z  FV N  x  FV M) :=
begin
  induction H; simp [sub, FV],
  case sub_is_def.var : y x N {
    split_ifs,
    { subst x, simp [eq_comm] },
    { simp [h, FV], rw and_iff_left_of_imp,
      intro, subst z, exact h } },
  case sub_is_def.app : P Q x N _ _ IH1 IH2 {
    simp [IH1, IH2, and_or_distrib_left,
      or_and_distrib_right, or.left_comm, or_assoc] },
  case sub_is_def.abs_same : y P x N e {
    subst y, simp [eq_comm, and_assoc, FV] },
  case sub_is_def.abs_diff_nel : y P x N h hx {
    simp [FV, h] at hx ,
    simp [sub_eq_self hx, hx],
    rw @and_iff_left_of_imp (_∧_),
    rintro h, _⟩ rfl, contradiction },
  case sub_is_def.abs_diff : y P x N h hy H IH {
    simp [FV, IH, h],
    by_cases zy : z = y; simp [zy, hy] }
end

Patrick Thomas (Jun 06 2019 at 02:57):

Thank you!

Patrick Thomas (Jun 11 2019 at 14:28):

Does this only use induction on H and no induction on M?

Patrick Thomas (Jun 22 2019 at 17:00):

What is the principle behind the induction on H? Would anyone have a recommendation for a resource describing this form of induction in general? How are the induction hypotheses determined?

Kevin Buzzard (Jun 22 2019 at 17:10):

If H is an inductive type, then Lean reads off the so-called recursor from the constructors of the type.

Kevin Buzzard (Jun 22 2019 at 17:11):

A good example to think about is nat. It's defined as: zero is a nat, the successor of a nat is a nat, and that's it. So the recursor is "if you can prove it for zero, and prove it for successors given a proof for all their inputs, you're done."

Kevin Buzzard (Jun 22 2019 at 17:12):

Another good example is false, the inductive proposition with no constructors. Because there are no constructors, the inductive principle is "you don't have to check anything and you can get a map from false to P for free", i.e. the statement that false implies P for all propositions P.

Kevin Buzzard (Jun 22 2019 at 17:12):

Maybe read TPIL, the chapter on inductive types?

Kevin Buzzard (Jun 22 2019 at 17:13):

I was really excited when I realised that one could do induction on the reals in Lean! And then it turned out that the theorem was: "if you can prove it for all Cauchy sequences of rationals, you can deduce it for all reals"!

Patrick Thomas (Jun 22 2019 at 17:16):

In this case H is not a type though right? It is a predicate?

Kevin Buzzard (Jun 22 2019 at 17:25):

A predicate can be an inductive type

Kevin Buzzard (Jun 22 2019 at 17:27):

I'm talking about the recursor attached to a general inductive type. You can do induction on an inductive type and the recursor is the function which does it for you

Kevin Buzzard (Jun 22 2019 at 17:27):

I think you can also do induction on a quotient type, you just end up doing things on the bigger type and checking they're constant on equivalence classes

Kevin Buzzard (Jun 22 2019 at 17:28):

You can't do induction on a pi type, and that's all the types there is

Kevin Buzzard (Jun 22 2019 at 17:29):

Apart from universes, if you want to call theem types, and you can't do induction on them either

Patrick Thomas (Jun 22 2019 at 17:33):

What is the principle for the recursor for an inductive type that is a predicate, in non formal terms? Is there a simple example like nat, but for a predicate?

Kevin Buzzard (Jun 22 2019 at 17:43):

Just make a simple inductive predicates called X and then do #check @X.rec

Kevin Buzzard (Jun 22 2019 at 17:43):

And and or are good ones

Kevin Buzzard (Jun 22 2019 at 17:44):

I'm on a bus right now with no access to lean. Aren't these things talked about in TPIL? there's an interesting treatment of them in that coq book logical foundations

Patrick Thomas (Jun 22 2019 at 17:59):

I may have to go back through the section in TPIL. I was looking for other introductory resources as well.

Kevin Buzzard (Jun 22 2019 at 20:26):

Try and figure out what the recursors for and and or are and then check in Lean.

Patrick Thomas (Jun 24 2019 at 22:57):

Is there a way to combine the following into a single justification for the second line?

have s3 : FV ( sub ( pre_term.var y ) x N ) = FV ( N ), by rw s2,
have s4 : z  FV ( N ), from s3  a2,

Kevin Buzzard (Jun 25 2019 at 06:25):

have s4 : z ∈ FV ( N ), from (show FV ( sub ( pre_term.var y ) x N ) = FV ( N ), by rw s2) ▸ a2, might work


Last updated: Dec 20 2023 at 11:08 UTC