Zulip Chat Archive

Stream: new members

Topic: Rewriting things inside ifs?


Guilherme Espada (Mar 17 2021 at 18:40):

In this example:

example : (if (0<1) then 1 else 2) = 1 := begin
  have proof: ((0:int)<(1:int))=true := by sorry,
  rw proof,
end

I don't see why the rewrite shouldn't work. What am I missing? What is a good way to handle ifs (both in the conclusion and in the premises)?

Thanks

Eric Wieser (Mar 17 2021 at 18:41):

Well for one, your theorem statement is about nat, but proof is about int

Eric Wieser (Mar 17 2021 at 18:41):

simp_rw proof works after you fix that

Guilherme Espada (Mar 17 2021 at 18:44):

Oh, simp_rw seems exactly what I was looking for. Another tool for my toolbelt.

Why doesn't rw behave like simp_rw?

Mario Carneiro (Mar 17 2021 at 18:46):

Because the condition of an if statement has a dependent argument, the proof that this condition is decidable, that needs to be rewritten as well

Mario Carneiro (Mar 17 2021 at 18:46):

rw doesn't handle this very well, it gives a motive is not type correct error in these cases

Guilherme Espada (Mar 17 2021 at 18:47):

Mario Carneiro said:

rw doesn't handle this very well, it gives a motive is not type correct error in these cases

That's exactly the error I was seeing when experimenting. Couldn't really understand it unfortunately

Guilherme Espada (Mar 17 2021 at 18:47):

Thank you both for your answers :smile:

Mario Carneiro (Mar 17 2021 at 18:47):

simp_rw has the same interface as rw but uses the proof strategy of simp, which is able to handle dependencies in some places (but may sometimes fail to rewrite where rw will work)

Guilherme Espada (Mar 18 2021 at 09:18):

Something weird is happening: simp_rw works correctly in the example posted above, and in another minimized example which I extracted with extract_goal:

import tactic.suggest
import data.finset
import data.finmap
import data.list
import data.finmap

@[derive decidable_eq]
inductive ttype
| ty_func (l r:ttype): ttype
| ty_bool : ttype

open ttype


instance : inhabited ttype := inhabited.mk ty_bool

@[derive decidable_eq]
inductive term
|t_var (idx:int) : term
|t_abs (T:ttype) (t:term) : term
|t_app (t1 t2: term) : term
|t_true : term
|t_false : term
|t_if (cond l r: term): term

open term

instance : inhabited term := inhabited.mk t_true

def upshift_cutoff : int  int  term  term
| d c (t_var k) := t_var (if (k<c) then k else (k + d))
| d c (t_abs T t) := t_abs T $ upshift_cutoff d (c+1) t
| d c (t_app t1 t2) := t_app (upshift_cutoff d c t1) (upshift_cutoff d c t2)
| d c t_true := t_true
| d c t_false := t_false
| d c (t_if cond l r) := t_if (upshift_cutoff d c cond) (upshift_cutoff d c l) (upshift_cutoff d c r)

def upshift (amt:int) (t1:term): term := upshift_cutoff amt 0 t1

def subst  : int  term  term  term
| idx s (t_var n) := if n = idx then s else t_var n
| idx s (t_abs T t) := t_abs T $ (subst (idx+1) (upshift 1 s) t)
| idx s (t_app t1 t2) := t_app (subst idx s t1) (subst idx s t2)
| idx s t_true := t_true
| idx s t_false := t_false
| idx s (t_if cond l r) := t_if (subst idx s cond) (subst idx s l) (subst idx s r)

inductive eval : term  term  Prop
| e_app1 {t1 t1' t2} : eval t1 t1'  eval (t_app t1 t2) (t_app t1' t2)
| e_app2 {t1 t2 t2'} : eval t2 t2'  eval (t_app t1 t2) (t_app t1 t2')
| e_appabs {T11 t12 v2} : eval (t_app (t_abs T11 t12) v2) (upshift (-1) (subst 0 (upshift 1 v2) t12))
| e_if_true {t2 t3} : eval (t_if t_true t2 t3) (t2)
| e_if_false {t2 t3} : eval (t_if t_false t2 t3) (t3)
| e_if {t1 t1' t2 t3} : eval t1 t1'  eval (t_if t1 t2 t3) (t_if t1' t2 t3)

example
  (b : eval
         ((t_abs ty_bool (t_abs ty_bool (t_var 0))).t_app (t_true.t_app t_true))
         (t_abs ty_bool (t_var (ite (0 < 1) 0 (-1))))) (kk : 0 < 1 = true) :
  eval ((t_abs ty_bool (t_abs ty_bool (t_var 0))).t_app (t_true.t_app t_true))
    (t_abs ty_bool (t_var 0)) :=
begin
  simp_rw kk at b, --works as expected
  simp at b,
  assumption,
end

However, in the more complex example I'm actually trying to prove, with the same context and goal as the example I posted above, it fails with simplify tactic failed to simplify (apologies for the huge example, but minification makes the issue disappear):

import tactic.suggest
import data.finset
import data.finmap
import data.list
import data.finmap
import tactic.linarith

@[derive decidable_eq]
inductive ttype
| ty_func (l r:ttype): ttype
| ty_bool : ttype

open ttype


instance : inhabited ttype := inhabited.mk ty_bool

@[derive decidable_eq]
inductive term
|t_var (idx:int) : term
|t_abs (T:ttype) (t:term) : term
|t_app (t1 t2: term) : term
|t_true : term
|t_false : term
|t_if (cond l r: term): term

open term

instance : inhabited term := inhabited.mk t_true

def upshift_cutoff : int  int  term  term
| d c (t_var k) := t_var (if (k<c) then k else (k + d))
| d c (t_abs T t) := t_abs T $ upshift_cutoff d (c+1) t
| d c (t_app t1 t2) := t_app (upshift_cutoff d c t1) (upshift_cutoff d c t2)
| d c t_true := t_true
| d c t_false := t_false
| d c (t_if cond l r) := t_if (upshift_cutoff d c cond) (upshift_cutoff d c l) (upshift_cutoff d c r)

def upshift (amt:int) (t1:term): term := upshift_cutoff amt 0 t1

def subst  : int  term  term  term
| idx s (t_var n) := if n = idx then s else t_var n
| idx s (t_abs T t) := t_abs T $ (subst (idx+1) (upshift 1 s) t)
| idx s (t_app t1 t2) := t_app (subst idx s t1) (subst idx s t2)
| idx s t_true := t_true
| idx s t_false := t_false
| idx s (t_if cond l r) := t_if (subst idx s cond) (subst idx s l) (subst idx s r)

inductive eval : term  term  Prop
| e_app1 {t1 t1' t2} : eval t1 t1'  eval (t_app t1 t2) (t_app t1' t2)
| e_app2 {t1 t2 t2'} : eval t2 t2'  eval (t_app t1 t2) (t_app t1 t2')
| e_appabs {T11 t12 v2} : eval (t_app (t_abs T11 t12) v2) (upshift (-1) (subst 0 (upshift 1 v2) t12))
| e_if_true {t2 t3} : eval (t_if t_true t2 t3) (t2)
| e_if_false {t2 t3} : eval (t_if t_false t2 t3) (t3)
| e_if {t1 t1' t2 t3} : eval t1 t1'  eval (t_if t1 t2 t3) (t_if t1' t2 t3)

abbreviation ctxtype := list ttype

def lookup_list : ctxtype  int  option ttype
| (head :: tail) 0  := some head
| (head :: tail) n  := lookup_list tail (n-1)
| list.nil _ := none
@[simp]
def in_ctx (idx: int) (val: ttype) (ctx:ctxtype) := lookup_list ctx idx = (some val)

inductive typ : ctxtype  term  ttype  Prop
| typ_var {ctx x T} : x0  in_ctx x T ctx  typ ctx (t_var x) T
| typ_abs {ctx T1 t2 T2} : typ (T1::ctx) t2 T2  typ ctx (t_abs T1 t2) (ty_func T1 T2)
| typ_app {ctx t1 T11 T12 t2} : (typ ctx t1 (ty_func T11 T12))  (typ ctx t2 T11)  typ ctx (t_app t1 t2) T12
| typ_true {ctx} : typ ctx t_true ty_bool
| typ_false {ctx} : typ ctx t_true ty_bool
| typ_if {ctx cond l r T} : typ ctx cond ty_bool -> typ ctx l T -> typ ctx r T  -> typ ctx (t_if cond l r) T

open typ

theorem subject_expansion_fails : ¬ ( ctx t t' T, typ ctx t' T  eval t t'  typ ctx t T) :=
begin
  simp,
  existsi (:ctxtype),
  existsi t_app (t_abs ty_bool (t_abs ty_bool (t_var 0))) (t_app t_true t_true),
  existsi t_abs  ty_bool (t_var 0),
  existsi ty_func ty_bool ty_bool,
  split,
  {
    refine typ_abs _,
    refine typ_var _ rfl,
    linarith,
  },
  split,
  {
    have a:= eval.e_appabs,
    have b: eval (t_app (t_abs ty_bool (t_abs ty_bool (t_var 0))) (t_true.t_app t_true)) (upshift (-1) (subst 0 (t_true.t_app t_true)  (t_abs ty_bool (t_var 0)))) := a,


    repeat{
      rw subst at b <|>
      rw upshift at b <|>
      rw upshift_cutoff at b <|>
      simp at b
    },
    have kk: (0<1)=true := by sorry,
    simp_rw kk at b, --error here


    rw subst at b,
    simp at b,
    rw upshift_cutoff at b,
    rw upshift_cutoff at b,
    exact b,
  },
  {
    intro a,
    cases a,
    cases a_ᾰ,
    cases a_ᾰ,
    cases a_ᾰ_1,
    cases a_ᾰ_1_ᾰ,
  }
end

Guilherme Espada (Mar 18 2021 at 09:20):

I can workaround the bug by extracting to a lemma and using that, but this behavior seems weird to me

Kevin Buzzard (Mar 18 2021 at 09:34):

Your mistake can be easily spotted if you use the infoview in VS code (and if you know what to look for -- this is a common slip ;-) ) and has nothing to do with Lean weirdness. Clicking on the 0 < 1 within the ite shows that it's an inequality of integers, and clicking on the one on kk shows that it's an inequality of naturals. So you can fix with

    have kk: ((0 : ) <1)=true := by sorry,
    simp_rw kk at b, -- works

Kevin Buzzard (Mar 18 2021 at 09:35):

PS have kk: ((0 : ℤ) <1)=true := by simp [zero_lt_one],

Kevin Buzzard (Mar 18 2021 at 09:36):

PPS the whole point of introducing those weird hard-to-type characters was to encourage users to name their own variables, users aren't supposed to be putting them in code.

Guilherme Espada (Mar 18 2021 at 10:12):

Thanks for the help. I have another question: Why does the behavior differ when I extract the lemma? Does lean automatically try to figure out the right kind of 0 somehow, which differs when the lemma is extracted?

Regarding , I'm trying to avoid it in new code. However, I have a large file that used autogenerated names (back when they used to be a) and it isn't really practical for me to rewrite it all.

Kevin Buzzard (Mar 18 2021 at 10:14):

Yes, Lean has a "figure out which zero it is" thing going on, it's called typeclass resolution applied to the class has_zero.zero. However, in contrast to most other pieces of notation, numerals have this "default type" of nat. Some of us would love to see the back of this, and if we had our way then your version of kk would simply fail with the error "I don't know which 0 you're talking about". But I think the designers had their reasons for this default to nat thing. All I see is the confusion it causes ;-)

Johan Commelin (Mar 18 2021 at 10:17):

Guilherme Espada said:

Regarding , I'm trying to avoid it in new code. However, I have a large file that used autogenerated names (back when they used to be a) and it isn't really practical for me to rewrite it all.

Is it practical for you to downgrade to an older version of lean? Or do you need the newest version of mathlib?

Guilherme Espada (Mar 18 2021 at 10:32):

Not really, the version I was using before had a bunch of broken things in visual studio. Besides, I really don't mind having to use ᾰ every now and then for old proofs, and I already went through and replaced the as with where needed. So at this point, I really wouldn't gain anything from downgrading.


Last updated: Dec 20 2023 at 11:08 UTC