feat: ApplyNewGoals config for apply
Lean.Meta.Tactic.apply now accepts an ApplyConfig argument. `apply` now changes which metavariables are stored with choice of the newGoals config. This allows one to implement `fapply` and `eapply`. An example of this is given in tests/lean/run/apply_tac.lean. Closes #1045
This commit is contained in:
parent
bf4ba1583d
commit
d954706fcf
3 changed files with 82 additions and 7 deletions
|
|
@ -61,19 +61,38 @@ private def dependsOnOthers (mvar : Expr) (otherMVars : Array Expr) : MetaM Bool
|
|||
let otherMVarType ← inferType otherMVar
|
||||
return (otherMVarType.findMVar? fun mvarId => mvarId == mvar.mvarId!).isSome
|
||||
|
||||
private def reorderNonDependentFirst (newMVars : Array Expr) : MetaM (List MVarId) := do
|
||||
let (nonDeps, deps) ← newMVars.foldlM (init := (#[], #[])) fun (nonDeps, deps) mvar => do
|
||||
/-- Partitions the given mvars in to two arrays (non-deps, deps)
|
||||
according to whether the given mvar depends on other mvars in the array.-/
|
||||
private def partitionDependentMVars (mvars : Array Expr) : MetaM $ Prod (Array MVarId) (Array MVarId) :=
|
||||
mvars.foldlM (init := (#[], #[])) fun (nonDeps, deps) mvar => do
|
||||
let currMVarId := mvar.mvarId!
|
||||
if (← dependsOnOthers mvar newMVars) then
|
||||
if (← dependsOnOthers mvar mvars) then
|
||||
pure (nonDeps, deps.push currMVarId)
|
||||
else
|
||||
pure (nonDeps.push currMVarId, deps)
|
||||
return nonDeps.toList ++ deps.toList
|
||||
|
||||
/-- Controls which new mvars are turned in to goals by the `apply` tactic.
|
||||
- `nonDependentFirst` mvars that don't depend on other goals appear first in the goal list.
|
||||
- `nonDependentOnly` only mvars that don't depend on other goals are added to goal list.
|
||||
- `all` all unassigned mvars are added to the goal list.
|
||||
-/
|
||||
inductive ApplyNewGoals where
|
||||
| nonDependentFirst | nonDependentOnly | all
|
||||
|
||||
def apply (mvarId : MVarId) (e : Expr) : MetaM (List MVarId) :=
|
||||
private def reorderGoals (mvars : Array Expr) : ApplyNewGoals → MetaM (List MVarId)
|
||||
| ApplyNewGoals.nonDependentFirst => do
|
||||
let (nonDeps, deps) ← partitionDependentMVars mvars
|
||||
return nonDeps.toList ++ deps.toList
|
||||
| ApplyNewGoals.nonDependentOnly => do
|
||||
let (nonDeps, deps) ← partitionDependentMVars mvars
|
||||
return nonDeps.toList
|
||||
| ApplyNewGoals.all => pure $ Array.toList $ mvars.map Lean.Expr.mvarId!
|
||||
|
||||
/-- Configures the behaviour of the `apply` tactic. -/
|
||||
structure ApplyConfig :=
|
||||
(newGoals := ApplyNewGoals.nonDependentFirst)
|
||||
|
||||
def apply (mvarId : MVarId) (e : Expr) (cfg : ApplyConfig := {}) : MetaM (List MVarId) :=
|
||||
withMVarContext mvarId do
|
||||
checkNotAssigned mvarId `apply
|
||||
let targetType ← getMVarType mvarId
|
||||
|
|
@ -89,8 +108,7 @@ def apply (mvarId : MVarId) (e : Expr) : MetaM (List MVarId) :=
|
|||
assignExprMVar mvarId (mkAppN e newMVars)
|
||||
let newMVars ← newMVars.filterM fun mvar => not <$> isExprMVarAssigned mvar.mvarId!
|
||||
let otherMVarIds ← getMVarsNoDelayed e
|
||||
-- TODO: add option `ApplyNewGoals` and implement other orders
|
||||
let newMVarIds ← reorderNonDependentFirst newMVars
|
||||
let newMVarIds ← reorderGoals newMVars cfg.newGoals
|
||||
let otherMVarIds := otherMVarIds.filter fun mvarId => !newMVarIds.contains mvarId
|
||||
let result := newMVarIds ++ otherMVarIds.toList
|
||||
result.forM headBetaMVarType
|
||||
|
|
|
|||
33
tests/lean/run/apply_tac.lean
Normal file
33
tests/lean/run/apply_tac.lean
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
import Lean
|
||||
open Lean
|
||||
open Lean.Elab
|
||||
open Lean.Meta
|
||||
open Lean.Elab.Tactic
|
||||
|
||||
example (a b c : Nat) (h₁ : a = b) (h₂ : b = c) : a = c := by
|
||||
apply Eq.trans _ h₂ -- the metavars created during elaboration become new goals
|
||||
trace_state
|
||||
exact h₁
|
||||
|
||||
example (a : Nat) : ∃ x, x = a := by
|
||||
apply Exists.intro -- the goal for the witness should occur "after" the goal for x = a
|
||||
trace_state
|
||||
rfl
|
||||
|
||||
elab "fapply " e:term : tactic =>
|
||||
evalApplyLikeTactic (Meta.apply (cfg := {newGoals := ApplyNewGoals.all})) e
|
||||
|
||||
elab "eapply " e:term : tactic =>
|
||||
evalApplyLikeTactic (Meta.apply (cfg := {newGoals := ApplyNewGoals.nonDependentOnly})) e
|
||||
|
||||
example (a : Nat) : ∃ x, x = a := by
|
||||
eapply Exists.intro -- only metavars with out forward dependencies are added as goals.
|
||||
trace_state
|
||||
rfl
|
||||
|
||||
example (a : Nat) : ∃ x, x = a := by
|
||||
fapply Exists.intro -- all unassigned metavars are added as new goals using the order they were created.
|
||||
trace_state
|
||||
exact a
|
||||
trace_state
|
||||
rfl
|
||||
24
tests/lean/run/apply_tac.lean.expected.out
Normal file
24
tests/lean/run/apply_tac.lean.expected.out
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
a b c : Nat
|
||||
h₁ : a = b
|
||||
h₂ : b = c
|
||||
⊢ a = b
|
||||
case h
|
||||
a : Nat
|
||||
⊢ ?w = a
|
||||
|
||||
case w
|
||||
a : Nat
|
||||
⊢ Nat
|
||||
case h
|
||||
a : Nat
|
||||
⊢ ?w = a
|
||||
case w
|
||||
a : Nat
|
||||
⊢ Nat
|
||||
|
||||
case h
|
||||
a : Nat
|
||||
⊢ ?w = a
|
||||
case h
|
||||
a : Nat
|
||||
⊢ a = a
|
||||
Loading…
Add table
Reference in a new issue