You can extend Lean with custom tactics/tools; this is metaprogramming.
Combinators help if you have to do the same thing multiple times, or recover from failures.
repeat
: applies argument repeatedly on all goals until it can’t be applied any furtheriterate
: applies argument repeatedly on first goal until it fails, and then stops<|>
(‘orelse’): tries first argument, and if it fails, applies second argumentall_goals
: applies argument once to each goal, succeeds only if succeeds on all goalsany_goals
: applies argument once to each goal, succeeds if succeeds on any goaltry
: transforms argument into tactic that never fails (i.e. try it and if you fail it’s ok)solve1
: if argument doesn’t prove the goal, failmeta
lets a function call other metafunctions.
Tactics have access to:
tactic.target
, tactic.get_goals
)tactic.local_context
, tactic.get_local ``hypothesis
)@[simp]
rules)You can print stuff with tactic.trace
.
run_cmd
can run a tactic.
For example, we can rewrite assumption
which looks through all hypotheses in the context and succeeds if it finds one that works with the goal.
meta def exact_list : list expr → tactic unit
| [] := tactic.fail "no matching hypothesis in environment"
| (h :: hs) :=
do { tactic.trace "trying",
tactic.trace h,
tactic.exact h }
<|> exact_list hs
meta def my_assumption : tactic unit :=
do
hs ← tactic.local_context,
exact_list hs
The framework revolves around five main types:
tactic
: manages proof state, global context, etcname
: represents structured nameexpr
: represents expression (term) as abstract syntax tree
expr.var
gets variables based on De Bruijn indicesexpr.lam
is a lambda expressionexpr.sort
are ‘universes’ (Sort 0
is Prop
, Sort 1
is Type
, etc)expr.local_const
is hypothesisexpr.pi
is used for pi binders (e.g. ∀)declaration
: represents constant declaration, definition, axiom, or lemmaenvironment
: stores all declarations and notations that make up global contextUnelaborated expressions (pre-expressions) omit some information, elaborated expressions have everything.
We can create literal expressions:
Similar for names:
Antiquotation lets you embed existing expression into larger expression, using prefix %%
and name from current context.
You can also use them in pattern matching, like (%%a + %%b = %%c) ← tactic.target