Zulip Chat Archive
Stream: lean4
Topic: hypothesis can't be used
Arthur Paulino (Apr 26 2022 at 18:35):
Hi! I want to apologize upfront for yet-another-long-mwe, but this time I am not being able to use a hypothesis in my context with simp
or rw
https://gist.github.com/arthurpaulino/22807b35054a638838974b73ad789315
The issue is that h'
can't be used on lines 394 and 395. I'm not entirely sure if I'm doing something wrong, but I suspect this should have worked
Thanks in advance for your attention :pray:
Leonardo de Moura (Apr 26 2022 at 18:41):
The following threads should clarify what is going on
https://leanprover.zulipchat.com/#narrow/stream/270676-lean4/topic/Circumventing.20.22motive.20not.20type-correct.22
https://leanprover.zulipchat.com/#narrow/stream/270676-lean4/topic/Cannot.20rewrite.20term.20being.20cased.20on
Leonardo de Moura (Apr 26 2022 at 18:42):
To minimize the problem, we have to change how we encode the match h : ... with ...
notation. It will happen, but not in the near future.
Arthur Paulino (Apr 26 2022 at 18:48):
Ah, alright, thanks!!
Let me read those :+1:
Leonardo de Moura (Apr 26 2022 at 18:48):
The second one is the most relevant.
James Gallicchio (Apr 26 2022 at 18:57):
Both those topics are mine, and I'm still pretty lost about how to do the right generalization -- maybe we can learn how to work around this together @Arthur Paulino :joy:
Arthur Paulino (Apr 26 2022 at 19:03):
Sure, from what I understood, I have to manually provide a motive for my consume
function (and I don't know what a motive is yet). Now that Leo has pointed out the root cause of the issue, I'll try to minimize my #mwe later and work on it. For now I'll just sorry that part out
Arthur Paulino (Apr 26 2022 at 19:04):
@James Gallicchio how far are you on the investigation?
James Gallicchio (Apr 26 2022 at 19:08):
Oh, actually, your case is maybe simple to solve, lemme try to fill this sorry in...
Julian Berman (Apr 26 2022 at 19:20):
Arthur Paulino said:
Sure, from what I understood, I have to manually provide a motive for my
consume
function (and I don't know what a motive is yet). Now that Leo has pointed out the root cause of the issue, I'll try to minimize my #mwe later and work on it. For now I'll just sorry that part out
Off-topic, but I don't know what a motive is precisely either yet, even though I know it's present in a particularly common Lean 3 error message. If you get a good handle on what one is, the glossary would benefit from having an entry for it!
James Gallicchio (Apr 26 2022 at 19:27):
I think a motive is a (potentially dependent) type which a recursor is eliminating into. The "target" type of a recursion/induction. So for inductive arguments trying to prove \forall n. P(n)
, the P
is the motive. Or for a recursive function from MyType
to Nat
, the motive is fun (_ : MyType) => Nat
James Gallicchio (Apr 26 2022 at 19:29):
James Gallicchio said:
Oh, actually, your case is maybe simple to solve, lemme try to fill this sorry in...
after spending 15 min on this i give up :joy: but it seems quite similar to the example Mario solved here
Mario Carneiro (Apr 26 2022 at 20:18):
Here's a fix for this proof:
theorem State.retProgression :
∃ n, (ret v c k^[n]).isEnd ∨ (ret v c k^[n]).isProg := by
induction k generalizing v c with
| app e es k hi =>
cases v with
| lam lm =>
cases lm with
| mk ns h p =>
cases h' : consume p ns es with
| none =>
refine ⟨1, ?_⟩
simp [stepN, step, h']
let isOk := fun (s : State) => s.isEnd ∨ s.isProg
suffices ∀ o h, o = none →
isOk (step.match_2 _ _ _ (fun x h' => State) o h _ _ _) from this _ rfl h'
intro o h h2; subst h2; simp
sorry
| some x =>
sorry
| _ => exact ⟨1, sorry⟩
| _ => sorry
Arthur Paulino (Apr 26 2022 at 20:25):
I don't understand that yet, but it gives me the errors:
application type mismatch
match ?m.40719 with
| Except.error m => ?m.40720 m
| Except.ok x => fun h' => State
argument
fun x h' => State
has type
(x : Value) → ?m.40727 x → Type : Sort (max 2 ?u.40724)
but is expected to have type
(v : Value) → ?m.40718 (Except.ok v) : Sort (imax 1 ?u.40717)
And
function expected at
match ?m.40719 with
| Except.error m => ?m.40720 m
| Except.ok v => ?m.40752 v
term has type
?m.40718 ?m.40719
Thanks for helping out :pray:
Mario Carneiro (Apr 26 2022 at 20:51):
It looks like some of the captured variables are different, try moving the (fun x h' => State)
argument earlier
Mario Carneiro (Apr 26 2022 at 20:52):
Here's the type of State.step.match_2
for me:
#check @State.step.match_2
State.step.match_2 : (es : NEList Expression) →
(ns : NEList String) →
(p : Program) →
(motive : (x : Option (Option (NEList String) × Program)) → consume p ns es = x → Sort u_1) →
(x : Option (Option (NEList String) × Program)) →
(h' : consume p ns es = x) →
((l : NEList String) →
(p_1 : Program) → (h' : consume p ns es = some (some l, p_1)) → motive (some (some l, p_1)) h') →
((p_1 : Program) → (h' : consume p ns es = some (none, p_1)) → motive (some (none, p_1)) h') →
((h' : consume p ns es = none) → motive none h') → motive x h'
Mario Carneiro (Apr 26 2022 at 20:54):
I obtained the term in the suffices
by setting set_option pp.match false
and then locating the (fun x h' => State)
(this is motive
), (consume p ns es)
(which is o
) and (_ : consume p ns es = consume p ns es)
(which is h
) arguments in the match expression in the goal
Mario Carneiro (Apr 26 2022 at 20:54):
I expect there is some lean version mismatch that has caused things to work out differently for you
Mario Carneiro (Apr 26 2022 at 20:55):
I'm running Lean (version 4.0.0-nightly-2022-04-16, commit 726b735c6df7, Release)
Arthur Paulino (Apr 26 2022 at 21:14):
I'm using today's nightly. Will check that soon!
Arthur Paulino (Apr 26 2022 at 21:31):
#check @State.step.match_2
-- State.step.match_2 : (motive : Except String Value → Sort u_1) →
-- (x : Except String Value) → ((m : String) → motive (Except.error m)) → ((v : Value) → motive (Except.ok v)) → motive x
James Gallicchio (May 02 2022 at 17:39):
Once again consulting the Mario oracle to find the correct chant of generalization to proceed in a proof:
constant UninitPointed.{u} (α : Type u) : NonemptyType.{u}
def Uninit.{u} (α : Type u) : Type u := (UninitPointed α).type
namespace Uninit
instance : Nonempty (Uninit α) := (UninitPointed α).property
unsafe def uninitUnsafe {α} : Uninit α := unsafeCast ()
@[implementedBy uninitUnsafe]
constant uninit {α} : Uninit α
unsafe def initUnsafe (a : α) : Uninit α := unsafeCast a
@[implementedBy initUnsafe]
constant init (a : α) : Uninit α
instance : Inhabited (Uninit α) := ⟨uninit⟩
noncomputable constant getValue? : Uninit α → Option α
def ofOption : Option α → Uninit α
| none => uninit
| some a => init a
axiom getValue_ofOption : ∀ {α} (a : Option α), getValue? (ofOption a) = a
axiom ofOption_getValue : ∀ {α} (a : Uninit α), ofOption (getValue? a) = a
noncomputable def isInit (a : Uninit α) : Bool := Option.isSome (getValue? a)
unsafe def getValueUnsafe (a : Uninit α) (h : (getValue? a).isSome) : α := unsafeCast a
@[implementedBy getValueUnsafe]
def getValue (a : Uninit α) (h : (getValue? a).isSome) : α :=
match getValue? a, h with
| some a, h => a
| none, h => by contradiction
set_option pp.match false
@[simp]
theorem getValue_init (h) : (init a).getValue h = a := by
unfold getValue
unfold getValue.match_1
sorry
James Gallicchio (May 02 2022 at 17:40):
I will attempt a few more random generalizations in the meantime
Leonardo de Moura (May 02 2022 at 18:01):
Here is an option using the generalize
tactic
@[simp]
theorem getValue_init (h) : (init a).getValue h = a := by
unfold getValue
generalize h' : getValue? (init a) = x, h = hx
split
next a h => sorry
next => contradiction
Leonardo de Moura (May 02 2022 at 18:07):
The notation h = hx
is a bit cryptic, but it is there because the type of h
depends on getValue? (init a)
. We should add a better notation for this.
Leonardo de Moura (May 02 2022 at 18:11):
We can also use h = h
, but it looks even more cryptic. BTW, the use of =
in the generalize
tactic is also a bit misleading. Any suggestions on how to improve this notation?
Mario Carneiro (May 02 2022 at 18:11):
Aside: Any chance generalize
can support at
? Mathlib has a generalize_hyp
command which does not do anything except this relatively straightforward generalization
Leonardo de Moura (May 02 2022 at 18:12):
Mario Carneiro said:
Aside: Any chance
generalize
can supportat
? Mathlib has ageneralize_hyp
command which does not do anything except this relatively straightforward generalization
Yes, we can add support for at
.
Mario Carneiro (May 02 2022 at 18:12):
Is that h = hx
syntax new in lean 4 generalize
? I don't recognize it from lean 3
Leonardo de Moura (May 02 2022 at 18:13):
generalize
in Lean 4 can take a telescope and simultaneously generalize many terms.
Mario Carneiro (May 02 2022 at 18:14):
how about hx := h
instead of h = hx
?
Mario Carneiro (May 02 2022 at 18:15):
the =
is backwards but I think we have precedent for using hx := <- h
at this point if we want it the other way around
Mario Carneiro (May 02 2022 at 18:15):
ah crap, maybe that doesn't work as well in lean 4 since that's actually an expression
Mario Carneiro (May 02 2022 at 18:16):
I suppose you can parse the arrow anyway, rw
does that too
Leonardo de Moura (May 02 2022 at 18:16):
What about the fact that the types of h
and hx
are different?
Mario Carneiro (May 02 2022 at 18:17):
oh I see... (x, h) := (e1, e2)
? Possibly without parens too
Mario Carneiro (May 02 2022 at 18:17):
it might be harder to read for a long telescope though
Mario Carneiro (May 02 2022 at 18:18):
maybe it's just better to live with the slightly mismatched types
Leonardo de Moura (May 02 2022 at 18:19):
Thanks for the feedback. The suggestions are already better than the current notation.
Mario Carneiro (May 02 2022 at 18:19):
:+1:
Leonardo de Moura (May 02 2022 at 18:19):
If you have more ideas, please keep posting them here.
Kyle Miller (May 02 2022 at 19:09):
Another idea: have the syntax look more like binders rather than patterns, like generalize (h : a = b) (e = f) (h' : c = d)
Leonardo de Moura (May 02 2022 at 23:16):
@Kyle Miller Thanks for sharing. I have a question. Some of the equalities are actually heterogeneous. Should we enforce it in the new notation? That is, sign an error if one uses =
, but the equality is heterogeneous.
James Gallicchio (May 03 2022 at 00:31):
Personally I'd prefer some sort of English which makes it read better, something like
generalize h' : getValue? (init a), h as x, hx
It would feel a bit more similar to the other tactics where you list new identifiers at the end
James Gallicchio (May 03 2022 at 00:32):
But thank you! The generalize tactic actually seems perfect for circumventing this issue
James Gallicchio (May 03 2022 at 00:38):
Wait, can you generalize multiple terms at once? If not, then I feel like the most natural would be
generalize h' : getValue? (init a) as x in h1, h2, ...
where the hypotheses h1, ...
keep their names but have new types
Arthur Paulino (May 03 2022 at 00:41):
Was it where Mario was getting at with the at
suggestion?
James Gallicchio (May 03 2022 at 00:49):
ooh potentially, I don't know what generalize_hyp does
Arthur Paulino (May 03 2022 at 00:55):
_hyp
in Lean 3 meta code base usually means that you're doing something with a variable in the context instead of with the target. The at
word is a good indicator that one can do at h1 h2 |-
or at *
(it's common syntax in mathlib)
Arthur Paulino (May 03 2022 at 00:58):
Using as
makes a lot of sense when you read it out loud. But I don't know other tactics that make use of "as" in their syntax (is this a concern?)
James Gallicchio (May 03 2022 at 01:00):
It's similar to ML binding patterns x as p
, where x
is bound to whatever matches p
Mario Carneiro (May 03 2022 at 05:28):
Arthur Paulino said:
Using
as
makes a lot of sense when you read it out loud. But I don't know other tactics that make use of "as" in their syntax (is this a concern?)
It is a concern, because these sorts of filler words have to be keywords for the most part, which precludes their use in identifiers. as
comes up quite a bit in theorems about lists where the lists are called as
and bs
Mario Carneiro (May 03 2022 at 05:29):
as a result it's generally best to stick to a somewhat restrictive grammar of filler words in tactics like with
, using
, from
, or more unusual words like generalizing
Mario Carneiro (May 03 2022 at 05:30):
set
is precedent for this kind of input, it uses set x : t := val with h
where h
receives the equation x = val
Mario Carneiro (May 03 2022 at 05:32):
and you could easily have a comma or indent-newline sequence of such things for generalize
Mario Carneiro (May 03 2022 at 05:35):
I think you could also add a type ascription on h
if you wanted to change the type of it to something defeq or get an error if it's a heq and you didn't expect that, e.g. generalize x := val with (h : x = val)
Mario Carneiro (May 03 2022 at 05:35):
I would probably leave the ascription off most of the time but it works syntactically
Leonardo de Moura (May 04 2022 at 00:53):
I just pushed a commit that improves the split
tactic. It uses a custom generalization procedure, and can now handle match
expressions such as match h : Q.F.force with ...
. It is not heavily tested yet, but can already handle examples by @James Gallicchio and @Arthur Paulino that used to fail.
https://github.com/leanprover/lean4/blob/94b5a9b460ce0a258b77503198f5319f8739e84a/tests/lean/run/james1.lean
https://github.com/leanprover/lean4/blob/94b5a9b460ce0a258b77503198f5319f8739e84a/tests/lean/run/arthur2.lean
From the first test:
def deq (Q : LazyBatchQueue τ) : Option (τ × LazyBatchQueue τ) :=
match h : Q.F.force with
| some (x, F') =>
some (x,
⟨F', Q.F_len - 1,
by simp [LazyList.F_force_some_len_minus_one h, Q.h_lens]⟩)
| none => none
theorem deq_correct (Q : LazyBatchQueue τ) : deq Q = none ↔ Q.F.force = none := by
simp [deq]
split <;> simp [*]
Arthur Paulino (May 04 2022 at 00:57):
Thanks!!!
Leonardo de Moura (May 04 2022 at 22:45):
BTW, the new generalizer for the split tactic can also handle the geValue
example above:
def getValue (a : Uninit α) (h : (getValue? a).isSome) : α :=
match getValue? a, h with
| some a, h => a
| none, h => by contradiction
@[simp]
theorem getValue_init (h) : (init a).getValue h = a := by
unfold getValue
split
next a' h' heq₁ heq₂ =>
/-
a' : α
h' : Option.isSome (some a') = true
heq₁ : getValue? (init a) = some a'
heq₂ : HEq h h'
⊢ a' = a
-/
sorry
next => contradiction
James Gallicchio (May 05 2022 at 09:50):
This is already helping me make lots of progress, thank you so much!
Evgeniy Kuznetsov (May 14 2022 at 10:26):
@Leonardo de Moura , just discovered nightly-2022-05-04
split regression:
class Foo (f: Type) where
foo : Option f
def bar (self: Foo f) := if let .some _ := self.foo then True else True
example [self: Foo f]:
bar self → True ∧ bar self := by
unfold bar
intro hi
constructor; trivial
split <;> simp
tactic 'split' failed
case right
f : Type
self : Foo f
hi :
match Foo.foo with
| some val => True
| x => True
⊢ match Foo.foo with
| some val => True
| x => True
Last updated: Dec 20 2023 at 11:08 UTC