References and bindings
Everything after by in a proof line is a rule application:
rule (bindings) [references]
The references supply proofs of the cited rule’s hypotheses, and the bindings instantiate its variables. This chapter describes both references and bindings, ending with inline applications — rule applications used as references.
The reference list
The brackets hold one reference per hypothesis of the cited rule, in the order the rule declares its hypotheses; a mismatched count is an error. Each reference is either:
- a hypothesis reference
#nor#name, citing a hypothesis of the theorem or lemma being proved; - a line reference, a bare label citing an earlier line of the same block; or
- an inline application, a rule applied on the spot (described below).
A bare identifier is always read as a line reference, even when a rule has the
same name. Writing h1 in a block with no line labeled h1 reports an unknown
label; applying the axiom in place requires the inline-application syntax, at
minimum h1 [].
Labels are local to their block. A proof cannot refer to a line in another block.
Hypothesis references
Hypotheses are numbered #1, #2, … in the order they appear in the
theorem’s header. A hypothesis declared as a named binder may also be cited by
name.
#hab and #hbc cite the two named hypotheses; #1 and #2 would be another
way of referring to those hypotheses. The arrow-form hypothesis $ a $ has no
name and can only be cited as #3. A # name that matches no hypothesis
binder is an error.
Bindings
A binding assigns an expression to one of the cited rule’s binders:
(name := $ expr $, ...)
Each name must be a binder of the rule, and each expression is written like any other math string, using the variables of the current block. The order of the bindings does not matter, but assigning the same binder twice or naming a binder the rule does not have is an error.
Bound binders are assigned the same way, but a {x} binder stands for a
variable, so it must be given a bare variable, not a compound expression.
Instantiations remain subject to the occurrence-based restrictions described in Variables, binders, and dependencies.
Omitted bindings
Bindings are usually omitted. The compiler infers each missing binder by matching the stated goal against the rule’s conclusion and the references against the rule’s hypotheses. Both proofs above check with their binding lists deleted, so you can hover a line once its cell checks to see what was inferred.
When the goal is written out and every reference is a hypothesis or an earlier line, the compiler matches complete formulas. This usually determines every variable the rule mentions. Undetermined binders arise mainly with inline applications, whose goals are not explicitly written down.
Inline applications
A reference may itself be a rule application, written with the same syntax
that follows by. It behaves like an anonymous proof line inserted just
before the line that uses it: the rule is applied, and its conclusion becomes
the reference expression. The hidden line has no label and cannot be cited
later; a result needed more than once should get a labeled line of its own.
Because a bare identifier is always a line reference, an inline application
must carry a reference list or a binding list. A rule with no hypotheses is
applied as h1 []; when a binding list is present, an empty reference list may
be dropped.
This is weaken from above with the h1 instance applied in place. Inline
applications nest, and mix freely with the other reference kinds.
An inline application has no written goal, so the compiler must infer its
entire conclusion. The enclosing application supplies the expected
conclusion from the stated goal, explicit bindings, and other references. In
weaken_inline, the goal and #1 fix both variables of mp, so h1 is
asked to prove p -> (q -> p) and its own variables are forced.
The expected conclusion need not be complete. A variable of the enclosing rule that is still unknown is left open in the hint, to be settled by the inline application’s own conclusion or by another reference — including one further to the right. Every variable must be determined somewhere in the line. If one remains unknown, the compiler rejects the line. Supply the missing value with a binding list on the inline application:
The compiler must resolve every binding in an inline application before it can finish checking the enclosing application. It does not try several possible values for an unresolved binder.
The unpack action described in The parts of a proof line reverses this notation. It creates one labeled line per inline application and fills each new goal from the checked conclusion.