Zulip Chat Archive
Stream: lean4
Topic: MVars from implicit variables
Adam Topaz (Oct 04 2023 at 18:17):
I'm trying to get a variant of an apply at
tactic to work. So far, I have the following:
import Mathlib.Tactic
import Lean
open Lean Meta Elab Tactic Term
/--
This function is similar to `forallMetaTelescopeReducingUntilDefEq` except that
it will construct mvars until it reaches one whose type is defeq to the given
type `t`. It uses `forallMetaTelescopeReducing`.
-/
def Lean.Meta.forallMetaTelescopeReducingUntilDefEq
(e t : Expr) (kind : MetavarKind := MetavarKind.natural) :
MetaM (Array Expr × Array BinderInfo × Expr) := do
let mut mvs : Array Expr := #[]
let mut bis : Array BinderInfo := #[]
let (ms, bs, tp) ← forallMetaTelescopeReducing e (some 1) kind
unless ms.size == 1 do throwError "Error"
mvs := ms
bis := bs
let mut out : Expr := tp
while !(← isDefEq (← inferType mvs.toList.getLast!) t) do
let (ms, bs, tp) ← forallMetaTelescopeReducing out (some 1) kind
unless ms.size == 1 do throwError "Error"
mvs := mvs ++ ms
bis := bis ++ bs
out := tp
return (mvs, bis, out)
namespace Mathlib.Tactic
elab "apply" t:term "at" i:ident : tactic => withMainContext do
let f ← Term.elabTerm t none
let ldecl ← (← getLCtx).findFromUserName? i.getId
let (mvs, _, tp) ← forallMetaTelescopeReducingUntilDefEq (← inferType f) ldecl.type
let mainGoal ← getMainGoal
let mainGoal ← mainGoal.tryClear ldecl.fvarId
let mainGoal ← mainGoal.assert ldecl.userName tp (mkAppN f (mvs.pop.push ldecl.toExpr))
let (_, mainGoal) ← mainGoal.intro1P
replaceMainGoal <| [mainGoal] ++ mvs.pop.toList.map fun e => e.mvarId!
example {α β γ : Type*} (f : ∀ {a : α}, β → γ) (b : β) : γ := by
apply f at b
-- ⊢ γ
sorry
I can't seem to figure out how to get a goal of type in the example with the current code. What am I missing here? If sorry
is replaced with assumption
, an error pops us at f
on the first line of the tactic block complaining that Lean doesn't know what to do with the term of alpha
, as expected. How would I arrange for this to be an additional goal?
Adam Topaz (Oct 04 2023 at 18:18):
Are the metavariables introduced by forallMetaTelescopeReducing
associated to implicit binders special somehow? Is that what's going on here?
Damiano Testa (Oct 04 2023 at 19:53):
Using
apply @f at b
works...
More seriously, maybe if you carefully craft a mkAppOptM'
application instead of your mkAppN
one, it might work? You would have to inspect all the arguments to f
and, for each, decide whether to pass it or not...
Adam Topaz (Oct 04 2023 at 19:56):
Right, I noticed that @f
works. I want something similar to the behavior of the usual apply
which introduces goals for implicit binders as well.
Adam Topaz (Oct 04 2023 at 19:57):
I'll take a look at mkAppOptM'
. Thanks!
Damiano Testa (Oct 04 2023 at 19:58):
If you use f.getAppFn
you might get closer to having a "meta" @f
?
Damiano Testa (Oct 04 2023 at 19:59):
E.g., look at the error message with this:
let mainGoal ← mainGoal.assert ldecl.userName tp (← mkAppOptM' f.getAppFn
(#[some (← mkFreshExprMVar none)] ++ (mvs.pop.push ldecl.toExpr).map fun x => some x))
-- error:
/-
application type mismatch
@f ?m.13218
argument
?m.13218
has type
?m.13217 : Sort ?u.13216
but is expected to have type
α : Type u_1
-/
Is this progress?
Adam Topaz (Oct 04 2023 at 20:10):
Where is the @
elaborator defined?
Damiano Testa (Oct 04 2023 at 20:11):
I do not know...
Adam Topaz (Oct 04 2023 at 20:12):
Ah it's in Lean/Parser/Term
.
Adam Topaz (Oct 04 2023 at 20:12):
well, the parser at least: docs#Lean.Parser.Term.explicit
Adam Topaz (Oct 04 2023 at 20:15):
LOL, here's a hack:
import Mathlib.Tactic
import Lean
open Lean Meta Elab Tactic Term
/--
This function is similar to `forallMetaTelescopeReducingUntilDefEq` except that
it will construct mvars until it reaches one whose type is defeq to the given
type `t`. It uses `forallMetaTelescopeReducing`.
-/
def Lean.Meta.forallMetaTelescopeReducingUntilDefEq
(e t : Expr) (kind : MetavarKind := MetavarKind.natural) :
MetaM (Array Expr × Array BinderInfo × Expr) := do
let mut mvs : Array Expr := #[]
let mut bis : Array BinderInfo := #[]
let (ms, bs, tp) ← forallMetaTelescopeReducing e (some 1) kind
unless ms.size == 1 do throwError "Error"
mvs := ms
bis := bs
let mut out : Expr := tp
while !(← isDefEq (← inferType mvs.toList.getLast!) t) do
let (ms, bs, tp) ← forallMetaTelescopeReducing out (some 1) kind
unless ms.size == 1 do throwError "Error"
mvs := mvs ++ ms
bis := bis ++ bs
out := tp
return (mvs, bis, out)
namespace Mathlib.Tactic
elab "apply" t:term "at" i:ident : tactic => withMainContext do
let t ← `(@$t)
let f ← Term.elabTerm t none
let ldecl ← (← getLCtx).findFromUserName? i.getId
let (mvs, _, tp) ← forallMetaTelescopeReducingUntilDefEq (← inferType f) ldecl.type
let mainGoal ← getMainGoal
let mainGoal ← mainGoal.tryClear ldecl.fvarId
let mainGoal ← mainGoal.assert ldecl.userName tp
(← mkAppOptM' f (mvs.pop.push ldecl.toExpr |>.map fun e => some e))
let (_, mainGoal) ← mainGoal.intro1P
replaceMainGoal <| [mainGoal] ++ mvs.pop.toList.map fun e => e.mvarId!
example {α β γ : Type*} (f : ∀ {a : α}, β → γ) (b : β) : γ := by
apply f at b
sorry
Damiano Testa (Oct 04 2023 at 20:21):
I do not know if it is awesome or terrible that this works.
Damiano Testa (Oct 04 2023 at 20:22):
-- let t ← `(@$t)
let f ← Term.elabTerm t none
let f := f.getAppFn
Adam Topaz (Oct 04 2023 at 20:24):
oh, but that's not the intended behavior, I think. What if f
is a partially applied function?
Damiano Testa (Oct 04 2023 at 20:25):
With this, I suspect that then you would have roll up your sleeves and collect all the previous arguments.
Damiano Testa (Oct 04 2023 at 20:28):
That could be information that the forallMetaTelescopeReducingUntilDefEqforallMetaTelescopeReducingUntilDefEqforallMetaTelescopeReducingUntilDefEqforallMetaTelescopeReducingUntilDefEqforallMetaTelescopeReducingUntilDefEqforallMetaTelescopeReducingUntilDefEqforallMetaTelescopeReducingUntilDefEqforallMetaTelescopeReducingUntilDefEqforallMetaTelescopeReducingUntilDefEqforallMetaTelescopeReducingUntilDefEq
gives back to you, along with everything else, maybe
Damiano Testa (Oct 04 2023 at 20:30):
In fact, a common situation would be one where f
has some typeclass assumptions, so even if you do not want this tactic to be extra-robust, you are right that this is an issue that you need to address.
Damiano Testa (Oct 04 2023 at 23:02):
EDIT: this is nonsense!
Adam, maybe this is closer to what you wanted?
let f := f.getAppFn
let initial_args := f.getAppArgs
let f := mkAppN f initial_args
Damiano Testa (Oct 04 2023 at 23:06):
It had not occurred to me until now that f
and mkAppN f f.getAppArgs
could not be the same expression.
Damiano Testa (Oct 05 2023 at 06:34):
This might be a way to build on Lean.MVarId.apply
:
elab "apply" t:term "at" i:ident : tactic => withMainContext do
let f ← Term.elabTerm t none
let ldecl ← (← getLCtx).findFromUserName? i.getId
let (mvs, _, tp) ← forallMetaTelescopeReducingUntilDefEq (← inferType f) ldecl.type -- no `mvs` needed
let mainGoal ← (← getMainGoal).assert ldecl.userName tp f
let (_, mainGoal) ← mainGoal.intro1P
let gs ← mainGoal.apply f
let gs := ← gs.mapM fun x => x.apply ldecl.toExpr <|> pure [x]
replaceMainGoal <| gs.join
Adam Topaz (Oct 05 2023 at 13:10):
(deleted) sorry what I said was wrong... working on it now.
Adam Topaz (Oct 05 2023 at 13:14):
Sigh. I"m still confused:
import Mathlib.Tactic
import Lean
open Lean Meta Elab Tactic Term
/--
This function is similar to `forallMetaTelescopeReducingUntilDefEq` except that
it will construct mvars until it reaches one whose type is defeq to the given
type `t`. It uses `forallMetaTelescopeReducing`.
-/
def Lean.Meta.forallMetaTelescopeReducingUntilDefEq
(e t : Expr) (kind : MetavarKind := MetavarKind.natural) :
MetaM (Array Expr × Array BinderInfo × Expr) := do
let mut mvs : Array Expr := #[]
let mut bis : Array BinderInfo := #[]
let (ms, bs, tp) ← forallMetaTelescopeReducing e (some 1) kind
unless ms.size == 1 do throwError "Error"
mvs := ms
bis := bs
let mut out : Expr := tp
while !(← isDefEq (← inferType mvs.toList.getLast!) t) do
let (ms, bs, tp) ← forallMetaTelescopeReducing out (some 1) kind
unless ms.size == 1 do throwError "Error"
mvs := mvs ++ ms
bis := bis ++ bs
out := tp
return (mvs, bis, out)
namespace Mathlib.Tactic
elab "apply" t:term "at" i:ident : tactic => withMainContext do
let f ← Term.elabTerm t none
let ldecl ← (← getLCtx).findFromUserName? i.getId
let (mvs, _, tp) ← forallMetaTelescopeReducingUntilDefEq (← inferType f) ldecl.type
logInfo m!"{mvs.size}"
let mainGoal ← getMainGoal
let mainGoal ← mainGoal.tryClear ldecl.fvarId
let mainGoal ← mainGoal.assert ldecl.userName tp
(← mkAppOptM' f (mvs.pop.push ldecl.toExpr |>.map fun e => some e))
let (_, mainGoal) ← mainGoal.intro1P
replaceMainGoal <| [mainGoal] ++ mvs.pop.toList.map fun e => e.mvarId!
example {α β γ : Type*} (f : {_ : α} → β → γ) (b : β) (a : α) : γ := by
run_tac withMainContext do
let lctx ← getLCtx
let f ← lctx.findFromUserName? `f
let b ← lctx.findFromUserName? `b
let (mvs, _, tp) ← forallMetaTelescopeReducingUntilDefEq f.type b.type
logInfo m!"{mvs.size}" -- 2
apply f at b -- 1
sorry
Adam Topaz (Oct 05 2023 at 14:03):
Ok, I think I minimized it down to the following:
import Mathlib.Tactic
import Lean
open Lean Meta Elab Tactic Term
def foo {n : Nat} (a : Nat × Nat) : Nat × Nat × Nat := (n,a)
example (bar : {n : Nat} → Nat × Nat → Nat × Nat × Nat) : True := by
run_tac withMainContext do
let foo ← Term.elabTerm (← `(foo)) none
let fdecl ← (← getLCtx).findFromUserName? `bar
let (mvs, _, tp) ← forallMetaTelescopeReducing (← inferType foo)
let (mvs', _, tp') ← forallMetaTelescopeReducing fdecl.type
logInfo m!"{mvs.size} {mvs'.size}" -- 1 2
return
sorry
I guess there's a difference in the behavior of forallMetaTelescopeReducing
when applied to the type of an elaborated term vs. the type of a local declaration. I still don't really understand why this is the case. Can someone explain the difference between the two, just for my understanding?
Adam Topaz (Oct 05 2023 at 14:08):
Aha, I guess the following is the reason:
import Mathlib.Tactic
import Lean
open Lean Meta Elab Tactic Term
def foo : {_ : Nat} → Nat × Nat → Nat × Nat × Nat := fun {n} a => (n,a)
example (bar : {n : Nat} → Nat × Nat → Nat × Nat × Nat) : True := by
run_tac withMainContext do
let foo ← Term.elabTerm (← `(foo)) none
let fdecl ← (← getLCtx).findFromUserName? `bar
logInfo m!"{fdecl.type}" -- {n : ℕ} → ℕ × ℕ → ℕ × ℕ × ℕ
logInfo m!"{← inferType foo}" -- ℕ × ℕ → ℕ × ℕ × ℕ
sorry
So maybe the `($f)
hack above wasn't so crazy?
Adam Topaz (Oct 05 2023 at 18:31):
I went with `($t)
: #7527
Adam Topaz (Oct 06 2023 at 01:54):
Damiano Testa said:
This might be a way to build on
Lean.MVarId.apply
:elab "apply" t:term "at" i:ident : tactic => withMainContext do let f ← Term.elabTerm t none let ldecl ← (← getLCtx).findFromUserName? i.getId let (mvs, _, tp) ← forallMetaTelescopeReducingUntilDefEq (← inferType f) ldecl.type -- no `mvs` needed let mainGoal ← (← getMainGoal).assert ldecl.userName tp f let (_, mainGoal) ← mainGoal.intro1P let gs ← mainGoal.apply f let gs := ← gs.mapM fun x => x.apply ldecl.toExpr <|> pure [x] replaceMainGoal <| gs.join
@Damiano Testa I'm confused about what you're trying to do here... in the assert
step is it not the case that f
needs to have type tp
?
Damiano Testa (Oct 06 2023 at 08:15):
Adam, I think that has I answered this in the other thread. Briefly, I wanted to create new mvars, to then use Lean.MVarId.apply
instead of mkAppWhatever
.
Last updated: Dec 20 2023 at 11:08 UTC