Zulip Chat Archive

Stream: new members

Topic: induction using user-defined recursors


Kyle Miller (Aug 24 2020 at 23:55):

I'm probably not supposed to be doing this in the first place, but I was trying to define a user-defined recursor because I thought it might help with some problem I was having (I might ask about this in another topic). I'm observing something rather strange when I try to use it with the induction tactic, where it comes up with an unexpected type. In the following, elems_iff_eq is proof that rec' is usable, and elems_iff_eq' is showing the strange behavior.

import data.sym2

universes u v

@[reducible, recursor 5]
def rec' {α : Type u} {β : sym2 α  Sort v}
   (f : Π (a : α × α), β a) (h :  (a b : α × α) (p : a  b),
      (eq.rec (f a) (quotient.sound p) : β b) = f b)
   (q : sym2 α) : β q :=
quot.rec f h q

variables {α : Type u}

lemma elems_iff_eq {x y : α} {z : sym2 α} (hne : x  y) :
  x  z  y  z  z = (x, y) :=
λ hx, hy, rec' (λ z₁, z₂ hx hy, sym2.eq_iff.mpr begin
  cases sym2.mem_iff.mp hx with hx hx; cases sym2.mem_iff.mp hy with hy hy; cc,
end) (by { intros, refl }) z hx hy

lemma elems_iff_eq' {x y : α} {z : sym2 α} (hne : x  y) :
  x  z  y  z  z = (x, y) :=
begin
  rintros hx, hy,
  induction z using rec',
  sorry
end

At the point of the sorry, the primary goal becomes

α : Type u,
x y : α,
hne : x  y,
z : (α × α) × α × α,
hx : x  z,
hy : y  z
 z = (x, y)

That z should be of type α × α, and nothing after that line is well-typed.

Reid Barton (Aug 24 2020 at 23:56):

I've also seen an ill-typed proof state when using induction with quotient

Reid Barton (Aug 24 2020 at 23:57):

I never looked into the cause though, since it was back in the dark ages

Kyle Miller (Aug 25 2020 at 00:06):

What I had been running into was that induction z using quotient.rec gives un-rewritable terms, and the reason seemed to be that the same setoid was being described in different ways (but I'm not completely certain). So, I thought maybe a new recursor that ensured I got exactly the setoid instance I wanted would help, but then I got behavior that was even more peculiar!

Here's an example where a rewrite fails even though it seems like it should work:

import data.sym2

universes u

variables {α : Type u}

lemma elems_iff_eq {x y : α} {z : sym2 α} (hne : x  y) :
  x  z  y  z  z = (x, y) :=
begin
  rintros hx, hy,
  induction z using quotient.rec,
  cases z with z₁ z₂,
  rw sym2.eq_iff, -- rewrite tactic failed
end

Mario Carneiro (Aug 25 2020 at 00:07):

usually that means that a previous tactic left an instantiated metavariable in the state

Kyle Miller (Aug 25 2020 at 00:09):

It looks like the first goal's context has no metavariables, but the second goal does have one because the construction hasn't been completed yet.

Kyle Miller (Aug 25 2020 at 00:10):

I'm just basing this off looking at set_option pp.all true. The first goal:

α : Type u,
x y : α,
hne : @ne.{u+1} α x y,
z₁ z₂ : α,
hx :
  @has_mem.mem.{u u} α (sym2.{u} α) (@sym2.has_mem.{u} α) x
    (@quotient.mk.{u+1} (prod.{u u} α α) (@setoid.r.{u+1} (prod.{u u} α α) (sym2.rel.setoid.{u} α))
       (@prod.mk.{u u} α α z₁ z₂)),
hy :
  @has_mem.mem.{u u} α (sym2.{u} α) (@sym2.has_mem.{u} α) y
    (@quotient.mk.{u+1} (prod.{u u} α α) (@setoid.r.{u+1} (prod.{u u} α α) (sym2.rel.setoid.{u} α))
       (@prod.mk.{u u} α α z₁ z₂))
 @eq.{u+1} (sym2.{u} α)
    (@quotient.mk.{u+1} (prod.{u u} α α) (@setoid.r.{u+1} (prod.{u u} α α) (sym2.rel.setoid.{u} α))
       (@prod.mk.{u u} α α z₁ z₂))
    (@quotient.mk.{u+1} (prod.{u u} α α) (sym2.rel.setoid.{u} α) (@prod.mk.{u u} α α x y))

Mario Carneiro (Aug 25 2020 at 00:11):

whoa, that mwe has a really weird bug in it. Put your cursor at the end of the cases line. You should see this:

2 goals
α : Type u,
x y : α,
hne : x  y,
z₁ z₂ : α,
hx : x  (z₁, z₂),
hy : y  (z₁, z₂)
 (z₁, z₂) = (x, y)

case h
α : Type u,
x y : α,
hne : x  y,
z b : α × α,
p : z  b
 _ = _

Now move the cursor to the start of the line and then back to the end, and now the second goal will say _ = )_ (yes, an unclosed parenthesis). Repeating the cursor motion results in _ = ) )_ and so on.

Mario Carneiro (Aug 25 2020 at 00:11):

This may involve an interaction with the rich goal view

Kyle Miller (Aug 25 2020 at 00:14):

emacs has only well-matched parentheses here. Maybe the react-like diff algorithm isn't updating the goal view correctly. (I'm under the impression that's how it's set up.)

Mario Carneiro (Aug 25 2020 at 00:16):

the bug also goes away if I switch to plain goal view, so I'm sure it's a factor

Kyle Miller (Aug 25 2020 at 00:16):

Oh, this seems like a good place to ask: what does ?m_1[b, hx, hy] mean? I've just assumed it meant a metavariable that somehow depends on its lexical context, but I haven't been able to look it up.

Mario Carneiro (Aug 25 2020 at 00:17):

it's called a delayed abstraction. It represents a metavariable applied to concrete arguments

Mario Carneiro (Aug 25 2020 at 00:17):

it's like ?f (2+2) where ?f is a metavariable

Mario Carneiro (Aug 25 2020 at 00:18):

Basically these are not available for unification, they are only constraints, i.e. if you find ?f (2+2) =?= 4 you aren't allowed to assume that ?f is the identity

Bryan Gin-ge Chen (Aug 25 2020 at 00:19):

cc: @Edward Ayers regarding the widget bug above.

Mario Carneiro (Aug 25 2020 at 00:25):

Anyway, here's how I suggest you proceed, assuming you aren't specifically trying to use induction using, which I don't think has ever worked well

lemma elems_iff_eq {x y : α} {z : sym2 α} (hne : x  y) :
  x  z  y  z  z = (x, y) :=
begin
  refine quotient.rec_on_subsingleton z _,
  rintros z₁, z₂ hx, hy,
  rw sym2.eq_iff,
end

Kyle Miller (Aug 25 2020 at 00:31):

I'll keep quotient.rec_on_subsingleton in mind. What I had been doing before is induction without using, then possibly fixing up the quot.mk's that would appear with change before doing rewrites. In this case, I had used apply rather than rw to avoid needing change: https://github.com/leanprover-community/mathlib/blob/simple_graphs2/src/data/sym2.lean#L145

induction is nice because it updates the types of everything that depends on the thing being inducted on, so it would be nice if it worked properly.

Mario Carneiro (Aug 25 2020 at 00:36):

You can still get most of that functionality with plain refine

Mario Carneiro (Aug 25 2020 at 00:36):

you have to include the things to be reverted manually in the term but the motive generalization is done automatically

Mario Carneiro (Aug 25 2020 at 00:37):

You can also use regular quotient.rec_on but in this case you are proving a proposition so there is no need

Mario Carneiro (Aug 25 2020 at 00:40):

like this:

lemma elems_iff_eq {x y : α} {z : sym2 α} (hne : x  y) :
  x  z  y  z  z = (x, y) :=
begin
  rintros hx, hy,
  refine quotient.ind (λ z hx hy, _) z hx hy,
  cases z with z₁ z₂,
  rw sym2.eq_iff,
end

Last updated: Dec 20 2023 at 11:08 UTC