Zulip Chat Archive
Stream: lean4
Topic: Use proof in custom tactic
Marcus Rossel (Dec 29 2021 at 21:23):
I'm trying to write a tactic that obtains a proof of some statement and then uses that proof to apply a lemma.
As an example:
theorem some_lemma {n : Nat} (h : n = 5) : n ≠ 10 := sorry
open Lean Elab.Tactic in
elab (name := solveNEq10) "solveNEq10" : tactic => do
...
let h : n = 5 := sorry
evalTactic (← `(tactic|exact some_lemma $h)) -- ?
The solveNEq10
should solve a a goal of the form n ≠ 10
. It's able to obtain a proof of n = 5
and is supposed to use the some_lemma
with that fact. I can't figure out how to actually use h
here though.
So far I've only used evalTactic
which works using Syntax
. Is that appropriate here, or are there other mechanisms I should be using?
Mario Carneiro (Dec 29 2021 at 22:16):
How does the let h : n = 5 := sorry
part work? It is mixing meta levels: I would expect something more like
elab (name := solveNEq10) "solveNEq10" : tactic => do
...
evalTactic (← `(tactic| (
let h : n = 5 := sorry
exact some_lemma h)))
Marcus Rossel (Dec 30 2021 at 10:48):
Mario Carneiro said:
How does the
let h : n = 5 := sorry
part work? It is mixing meta levels: I would expect something more likeelab (name := solveNEq10) "solveNEq10" : tactic => do ... evalTactic (← `(tactic| ( let h : n = 5 := sorry exact some_lemma h)))
I guess I'll have to explain my real world project a bit more for this. Like in lean4-maze I'm defining a game that is played by proving that a level (an instance of Game
) is completable (using a sequence of moves):
structure Game where ...
inductive Move | up | down | left | right
-- Performing a move can fail!
def Game.move : Game → Move → Option Game := ...
inductive Game.completable : Game → Prop
| completed {g} : <completion conditions> → completable g
| move {g₁ g₂ : Game} {m} : (g₁.move m = some g₂) → completable g₂ → completable g₁
let level1 : Game := ...
theorem level1_completable : level1.completable := by
move Move.up -- This move tactic is what I'm trying to define.
move Move.left
...
I'm now trying to define the move
tactic, such that it:
- Gets the
Game
instance (let's call itg
) for which we're trying to proveg.completable
. - Performs the move (let's call it
m
) that was passed to the tactic:let g? := g.move m
. - If the move was invalid (
g? = Option.none
), fails. - If the move was successful (
g? = some g'
), use this fact (this is theh
from before) to applyGame.completable.move
.
The remaining goal would then require us to prove the remaining condition for Game.completable.move
, which is to show that g'
is also completable.
I don't see how to do this in "pure syntax", since I wouldn't know how to get hold of g
and hence let g? := g.move m
.
Sebastian Ullrich (Dec 30 2021 at 12:04):
What's wrong with doing apply Game.completable.move (m := Move.up) rfl
inside the quotation? It should succeed iff move Move.up
evaluates to some _
. If you want a nicer error message on failure, you can additionally evaluate move
outside of the quotation as you apparently planned to do, but these two move
invocations are otherwise independent. Because of proof irrelevance, it is not directly possible to reflect a proof back into syntax, i.e. we cannot implement Quote p
for p : Prop
.
Marcus Rossel (Dec 30 2021 at 12:27):
Sebastian Ullrich said:
What's wrong with doing
apply Game.completable.move (m := Move.up) rfl
inside the quotation? It should succeed iffmove Move.up
evaluates tosome _
. If you want a nicer error message on failure, you can additionally evaluatemove
outside of the quotation as you apparently planned to do, but these twomove
invocations are otherwise independent. Because of proof irrelevance, it is not directly possible to reflect a proof back into syntax, i.e. we cannot implementQuote p
forp : Prop
.
If I do:
open Lean Elab.Tactic in
elab (name := move) "move " m:term : tactic => do
evalTactic (← `(tactic| apply Game.completable.move (m := $m) rfl))
def level1 : Game := ...
theorem level1_completable : level1.completable := by
move Move.up
I get :
application type mismatch
Game.completable.move rfl
argument
rfl
has type
Game.move ?m.2730 Move.up = Game.move ?m.2730 Move.up : Prop
but is expected to have type
Game.move ?m.2730 Move.up = Option.some ?m.2731 : Prop
(Side Note: In my real project I'm not using Option
but a Result
type as the return type from Game.move
.)
Marcus Rossel (Dec 30 2021 at 12:28):
And in order to prove Game.move ?m.2730 Move.up = Option.some ?m.2731
I would need Lean to execute/reduce the call to Game.move
completely, because then it would see that the result is some _
.
But I don't know how to express that.
Marcus Rossel (Dec 30 2021 at 12:31):
I've thought about simping with all the definitions involved in Game.move
, but that got out of hand quickly.
The decide
tactic also doesn't work because expected type must not contain free or meta variables
.
Sebastian Ullrich (Dec 30 2021 at 13:11):
My bad, you'll want something like refine Game.completable.move (m := $m) rfl ?_
instead of apply
so that the Game
term gets inferred from the goal
Marcus Rossel (Dec 30 2021 at 13:25):
Hm, so in this MWE your approach works perfectly:
structure Game where
x : Nat
y : Nat
inductive Move | up | down | left | right
def Game.move (g : Game) : Move → Option Game
| Move.up => some ⟨g.x, g.y + 1⟩
| Move.right => some ⟨g.x + 1, g.y⟩
| Move.down => if g.y == 0 then none else some ⟨g.x, g.y - 1⟩
| Move.left => if g.x == 0 then none else some ⟨g.x - 1, g.y⟩
inductive Game.completable : Game → Prop
| completed {g} : g.x > 5 → completable g
| move {g₁ g₂ : Game} {m} : (g₁.move m = some g₂) → completable g₂ → completable g₁
def level1 : Game := ⟨0, 0⟩
open Lean Elab.Tactic Game.Move in
elab (name := move) "move " m:term : tactic => do
evalTactic (← `(tactic| refine Game.completable.move (m := $m) rfl ?_))
theorem level1_completable : level1.completable := by
move Move.right
move Move.right
move Move.right
move Move.right
move Move.right
move Move.right
simp [Game.completable.completed]
Marcus Rossel (Dec 30 2021 at 13:28):
In my real world project I still get:
application type mismatch
Game.completable.move rfl
argument
rfl
has type
Game.move level1 Move.up = Game.move level1 Move.up : Prop
but is expected to have type
Game.move level1 Move.up = Game.Move.Result.success ?m.2747 : Prop
Sebastian Ullrich (Dec 30 2021 at 13:32):
Hard to say without knowing more about move
. Are you positive that term does in fact evaluate to success _
? :)
Marcus Rossel (Dec 30 2021 at 13:36):
Unfortunately Game.move
is too big to reduce sensibly. But I can do:
#eval (level1.move Move.up).get!
And get a success _
.
Does the fact that Game.move
is partial
mess things up?
Sebastian Ullrich (Dec 30 2021 at 13:37):
Yes! You cannot prove anything about partial
.
Marcus Rossel (Dec 30 2021 at 13:38):
Thanks for your patience :D
Last updated: Dec 20 2023 at 11:08 UTC