Zulip Chat Archive

Stream: general

Topic: pointing the equation compiler right


James Wood (May 13 2018 at 11:58):

Hi, I'm new to Lean. I'm trying to write the following, but termination checking fails for weaken_term in the second recursive call for app and the recursive call for lam. Each time, the reported problem is that m does not decrease, but that should be irrelevant because the induction is on the term. How can I give Lean this hint? Or is the Lean termination checker not sufficiently Foetus-like, so I have to do something else?

open nat

inductive fin' : nat → Type
| zero {n} : fin' (succ n)
| succ {n} : fin' n → fin' (succ n)
open fin'

inductive dir : Type
| syn : dir
| chk : dir
open dir

inductive term : nat → dir → Type
| var {n} (i : fin' n)                      : term n syn
| app {n} (e : term n syn) (s : term n chk) : term n syn
| lam {n} (s : term (succ n) chk)           : term n chk
open term

def plus : nat → nat → nat
| zero n := n
| (succ m) n := succ (plus m n)

def weaken_fin' {n} : ∀ m, fin' (plus m n) → fin' (plus m (succ n))
| zero i := succ i
| (succ m) zero := zero
| (succ m) (succ i) := succ (weaken_fin' m i)

def weaken_term {n} : ∀ m {d}, term (plus m n) d → term (plus m (succ n)) d
| m syn (var i) := var (weaken_fin' m i)
| m syn (app e s) := app (weaken_term m e) (weaken_term m s)
| m chk (lam s) := lam (weaken_term (succ m) s)

Mario Carneiro (May 13 2018 at 12:15):

It can't be done directly by induction on term, since you would forget the relation to m and n

Mario Carneiro (May 13 2018 at 12:16):

Here's a way to write that you want to do induction explicitly with the equation compiler:

def weaken_term' {n} : ∀ {k d}, term k d → ∀ m, plus m n = k → term (plus m (succ n)) d
| _ syn (var i) m rfl := var (weaken_fin' m i)
| _ syn (app e s) m rfl := app (weaken_term' e m rfl) (weaken_term' s m rfl)
| _ chk (lam s) m rfl := lam (weaken_term' s (succ m) rfl)

def weaken_term {n} (m) {d} (s : term (plus m n) d) : term (plus m (succ n)) d :=
weaken_term' s m rfl

James Wood (May 13 2018 at 12:24):

Thanks! Where exactly is the relation to m and n being lost? I can't quite pick out why the term is allowed to depend on the index d like it does.

Mario Carneiro (May 13 2018 at 12:33):

It might help to try using the induction tactic to get a sense of what lean is doing under the hood

Mario Carneiro (May 13 2018 at 12:45):

def weaken_term {n} : ∀ m {d}, term (plus m n) d → term (plus m (succ n)) d :=
begin
  intros m d s,
  induction s with n' i n' e s IH1 IH2 n' s IH,
  exact var (weaken_fin' m i), --fail
end.

Notice that you won't be able to prove the first goal because the type is fin' n' instead of fin' (plus m n). This is where the equality assumption comes in

Mario Carneiro (May 13 2018 at 12:48):

You can actually prove this directly in tactic mode by starting with

  intros m d s,
  generalize h : plus m n = k,
  induction s with n' i n' e s IH1 IH2 n' s IH generalizing k; subst k,

which has the effect of introducing the equality before doing the induction

James Wood (May 13 2018 at 12:50):

Can the problem be restated in terms of induction principles, rather than tactics? If so, I think I can work that out.

Mario Carneiro (May 13 2018 at 12:53):

Look at the proof produced by the naive induction tactic approach:

def weaken_term {n} : ∀ m {d}, term (plus m n) d → term (plus m (succ n)) d :=
begin
  intros m d s,
  induction s; admit
end
set_option pp.implicit true
#print weaken_term
def weaken_term : Π {n : ℕ} (m : ℕ) {d : dir}, term (plus m n) d → term (plus m (succ n)) d :=
λ {n : ℕ} (m : ℕ) (d : dir) (s : term (plus m n) d),
  (λ (s : term (plus m n) d),
     @term.rec (λ (_x : ℕ) {d : dir} (s : term _x d), term (plus m (succ n)) d)
       (λ {s_n : ℕ} (s_i : fin' s_n), sorry)
       (λ {s_n : ℕ} (s_e : term s_n syn) (s_s : term s_n chk) (s_ih_e : term (plus m (succ n)) syn)
        (s_ih_s : term (plus m (succ n)) chk), sorry)
       (λ {s_n : ℕ} (s_s : term (succ s_n) chk) (s_ih : term (plus m (succ n)) chk), sorry)
       (plus m n)
       d
       s)
    s

Mario Carneiro (May 13 2018 at 12:53):

Notice that the motive to the term.rec is λ (_x : ℕ) {d : dir} (s : term _x d), term (plus m (succ n)) d

Mario Carneiro (May 13 2018 at 12:54):

In other words, it has assumed you are trying to prove def not_true {n} : ∀ m {d} k, term k d → term (plus m (succ n)) d

Mario Carneiro (May 13 2018 at 12:55):

where it has "forgotten" that the k there is actually plus m n

Mario Carneiro (May 13 2018 at 13:02):

By the way, something seems off about your inductive:

theorem no_chk {n} : term n chk → false :=
begin
  generalize h : chk = d,
  intro s,
  induction s generalizing h; injection h,
  exact s_ih rfl
end

James Wood (May 13 2018 at 13:03):

That's fixed now.

James Wood (May 13 2018 at 13:24):

You can actually prove this directly in tactic mode by starting with
...
which has the effect of introducing the equality before doing the induction

I tried doing this, but I get a lot of weird-looking errors, like “vm check failed”, “invalid 'begin-end' expression, ',' expected”, and “sync”.

James Wood (May 13 2018 at 13:25):

Code (supporting definitions have changed a bit):

def weaken_term' {m} : ∀ n {d}, term' (m + n) d → term' ((m + 1) + n) d :=
begin
  intros n d t,
  generalize h : m + n = k
  induction t with n' i n' e s ihe ihs n' s ih generalizing k; subst k,
  { _ },
  { _ },
  { _ },
  { _ },
  { _ }
end

Mario Carneiro (May 13 2018 at 13:27):

You shouldn't use { _ } as a tactic. Lean doesn't like being asked to run tactics that are not yet determined

Mario Carneiro (May 13 2018 at 13:27):

instead you can just write { }

Mario Carneiro (May 13 2018 at 13:28):

meaning "run no tactic", and then it will fail at the right bracket because you aren't done yet

James Wood (May 13 2018 at 13:28):

Aah, thanks.

James Wood (May 13 2018 at 13:28):

The problem seemed to be a missing comma, so that's sorted.

James Wood (May 13 2018 at 13:31):

Is _ the best way to leave a hole in a pattern-matching definition?

James Wood (May 13 2018 at 13:31):

Or is there some way to introduce an interactive hole?

Mario Carneiro (May 13 2018 at 13:32):

I don't quite understand what you mean

Mario Carneiro (May 13 2018 at 13:33):

I can't think of what an interactive hole is, but _ is useful as a wildcard in patterns

James Wood (May 13 2018 at 13:34):

Hole à la Agda (or Idris, to some extent).

James Wood (May 13 2018 at 13:37):

Maybe it's not necessary with the way lean-mode works, though I still feels like a hack to use the “this should be inferrable by unification” thing to mean “I'm going to write something here”.

Mario Carneiro (May 13 2018 at 13:39):

Lean actually has holes in term mode, {! !}, but they are not well developed at all and never made it past alpha

Mario Carneiro (May 13 2018 at 13:39):

for the most part you can use begin end or _ where stuff is expected


Last updated: Dec 20 2023 at 11:08 UTC