Zulip Chat Archive
Stream: general
Topic: How to unwrap a let expression
Bolton Bailey (Jul 01 2023 at 00:14):
I am in tactic mode, and I have a value defined via let
, which appears in my hypothesis list as g': Polynomial (ZMod q) := p % vanishingPoly G
. Now I have g'
in my goal and I want to see it as the value it's defined as again. How do I do this?
Kevin Buzzard (Jul 01 2023 at 00:16):
You can use change
to do it explicitly, or you can just imagine that it's being translated into its def and things might work fine anyway.
Bolton Bailey (Jul 01 2023 at 00:17):
The change
documentation looks like
change tgt' will change the goal from tgt to tgt', assuming these are definitionally equal.
change t' at h will change hypothesis h : t to have type t', assuming assuming t and t' are definitionally equal
Bolton Bailey (Jul 01 2023 at 00:18):
Do I have to rewrite out the whole goal? Why isn't there just a simple tactic where I can say unlet g'
and be done?
Kevin Buzzard (Jul 01 2023 at 00:19):
You need to use set
if you want that.
Kevin Buzzard (Jul 01 2023 at 00:19):
You set g := blah with hg
and now you can just rw [hg]
Kevin Buzzard (Jul 01 2023 at 00:20):
You don't have to rewrite the entire goal, you can just use _
s liberally. But set
is easier.
Bolton Bailey (Jul 01 2023 at 00:20):
Wait, so then what is the difference between set and let and why would I ever use let in tactic mode?
Bolton Bailey (Jul 01 2023 at 00:24):
Actually strike the cross out, I'm definitely confused about why you would use let in tactic mode. Is there ever a reason?
Bolton Bailey (Jul 01 2023 at 00:41):
Is it that let is not actually a tactic, but a lean built-in that somehow seems like a tactic and has tactic documentation?
Niels Voss (Jul 01 2023 at 00:45):
let
is both a built-in and a tactic. The tactic let
is essentially just a wrapper around the let
built-in that lets you use it in tactic mode (kind of like how have
is both a command and a tactic).
let
is very similar to have
in tactic mode, with the main difference that let
remembers what the term is defined as. If you do let x := 2
then x
is now definitionally equal to 2
so you can prove that x = 2
with just rfl
. This is not the case when you use have
, because if you did have x := 2
then there's no way to prove that x = 2
.
Niels Voss (Jul 01 2023 at 00:48):
set
is very similar to let
, but if you do set x := some_expression
then it will look at all your hypotheses and replace all instances of some_expression
with x
. Essentially you can use set as a replacement for generalize
. As Kevin Buzzard mentioned, another helpful thing about set
is that if you do set g := blah with hg
then it will also give you a proof that g = blah
Bolton Bailey (Jul 01 2023 at 00:56):
Thanks Niels, one more question: I see that there is a set!
tactic that does not replace the expression. Does this mean that set!
behaves identically to let
?
Niels Voss (Jul 01 2023 at 00:59):
I actually don't know. The only difference I can see between set!
and let
is that set!
lets you do set! x := 2 with hx
to automatically prove that x = 2
. I think this is basically just the same thing as let x := 2, have hx : x = 2 := rfl
but there might be some other difference I'm not aware of.
Bolton Bailey (Jul 01 2023 at 01:00):
Ok, thanks for the explanation and thanks to Kevin for solving my issue, you all have been a great help.
Niels Voss (Jul 01 2023 at 01:02):
Generally I like to use let
to introduce new terms that represent data, and have
to introduce new terms that represent proofs. And then I work with let
statements by using change
quite often. I only started learning lean last year, and when I did I had a lot of trouble with this same thing.
Bolton Bailey (Jul 01 2023 at 01:09):
My top-level issue is that I have change g' with p % vanishingPoly G
in my ported code, and I'm getting the error "tactic 'Lean.Parser.Tactic.changeWith' has not been implemented"
Niels Voss (Jul 01 2023 at 01:18):
I don't have much experience with lean 4 but from reading the lean 3 documentation it seems that if your goal is something like f g' = something
then change g' with p % vanishingPoly G
is just a shorter way to accomplish change f (p % vanishingPoly G) = something
. Basically you just write change ...
where the ...
is what you want your goal to become. Do you have a minimum working example?
Niels Voss (Jul 01 2023 at 01:28):
What I think the change g' with (p % vanishingPoly G)
command does is just replace all instances of g'
with (p % vanishingPoly G)
in the goal (I didn't actually know this existed) but if you can't use that you should just write out change ...
where ...
is what the goal would look like if you were to just replace all instances of g'
with (p % vanishpingPoly G)
. You could also consider asking about this in the #lean4 stream.
Bolton Bailey (Jul 01 2023 at 01:32):
Sorry, actually I'm mistaken, the change with
issue can be ameliorated by just commenting out the tactic. My issue now is that when I switch let to set to try to fix another part of the code, another part of the code starts timing out at isDefEq
, but indeed this seems like a problem for the lean4 stream.
Kyle Miller (Jul 02 2023 at 00:54):
@Bolton Bailey I couldn't find anything in Lean 4 or mathlib that lets you substitute let bindings, but it's possible to write this unfold_let
tactic:
import Mathlib.Tactic.Basic
open Lean Meta Elab.Tactic
/-- Unfold all the fvars from `fvars` in `e` that are local declarations. -/
def unfoldFVars (e : Expr) (fvars : Array FVarId) : MetaM Expr := do
transform e fun node =>
match node with
| .fvar fvarId =>
if fvars.contains fvarId then
(TransformStep.continue ·) <$> (fvarId.getValue? >>= Option.mapM instantiateMVars)
else
pure TransformStep.continue
| _ => pure TransformStep.continue
/--
`unfold_let x y z at loc` unfolds the local declarations `x`, `y`, and `z` at the given
location.
-/
syntax "unfold_let" (ppSpace colGt term:max)+ (ppSpace Parser.Tactic.location)? : tactic
elab_rules : tactic
| `(tactic| unfold_let $hs:term* $[$loc?]?) => withMainContext do
let fvarIds ← getFVarIds hs
withLocation (expandOptLocation (Lean.mkOptionalNode loc?))
(atLocal := fun h => do
liftMetaTactic1 fun mvarId => do
let ty ← instantiateMVars (← h.getType)
mvarId.changeLocalDecl' h (← unfoldFVars ty fvarIds))
(atTarget := liftMetaTactic1 fun mvarId => do
let ty ← instantiateMVars (← mvarId.getType)
mvarId.change (← unfoldFVars ty fvarIds))
(failed := fun _ => throwError "unfold_let failed")
example : let x := 1; let y := 2; x + y = y + x := by
intro x y
/-
x: Nat := 1
y: Nat := 2
⊢ x + y = y + x
-/
unfold_let x
-- ⊢ 1 + y = y + 1
Kyle Miller (Jul 02 2023 at 00:55):
Unless there's some other way to substitute this that I'm missing, I can try contributing this to mathlib
Kyle Miller (Jul 02 2023 at 01:03):
I'll mention that dsimp only
will substitute everything defined with let
indiscriminately
Bolton Bailey (Jul 02 2023 at 14:19):
This is helpful to know, there was a simp only [g']
in the ported code which was not working, but dsimp only
seems to make this part work.
Kyle Miller (Jul 04 2023 at 23:53):
This has made it into #5717, among a few other tactics (@Jeremy Salwen it also has eta_reduce
)
Jeremy Salwen (Jul 05 2023 at 15:52):
Kyle Miller said:
This has made it into #5717, among a few other tactics (Jeremy Salwen it also has
eta_reduce
)
Would an eta for structures tactic belong here as well?
Last updated: Dec 20 2023 at 11:08 UTC