text‹
Any variable that is not explicitly bound by ‹λ›-abstraction is considered
as ``free''. Logically, free variables act like outermost universal
quantification at the sequent level: ‹A1(x), …, An(x) ⊨ B(x)› means that
the result holds 🪙‹for all› values of ‹x›. Free variables for terms (not
types) can be fully internalized into the logic: ‹⊨ B(x)› and ‹⊨∧x. B(x)›
are interchangeable, provided that ‹x› does not occur elsewhere in the
context. Inspecting ‹⊨∧x. B(x)› more closely, we see that inside the
quantifier, ‹x› is essentially ``arbitrary, but fixed'', while from outside
it appears as a place-holder for instantiation (thanks to ‹∧› elimination).
The Pure logic represents the idea of variables being either inside or
outside the current scope by providing separate syntactic categories for 🪙‹fixed variables› (e.g.\ ‹x›) vs.\ 🪙‹schematic variables› (e.g.\ ‹?x›).
Incidently, a universal result ‹⊨∧x. B(x)› has the HHF normal form ‹⊨
B(?x)›, which represents its generality without requiring an explicit
quantifier. The same principle works for type variables: ‹⊨ B(?α)›
represents the idea of ``‹⊨∀α. B(α)›'' without demanding a truly
polymorphic framework.
🪙
Additional care is required to treat type variables in a way that
facilitates type-inference. In principle, term variables depend on type
variables, which means that type variables would have to be declared first.
For example, a raw type-theoretic framework would demand the context to be
constructed in stages as follows: ‹Γ = α: type, x: α, a: A(x\α)›.
We allow a slightly less formalistic mode of operation: term variables ‹x›
are fixed without specifying a type yet (essentially 🪙‹all› potential
occurrences of some instance ‹x\τ› are fixed); the first occurrence of ‹x›
within a specific term assigns its most general type, which is then
maintained consistently in the context. The above example becomes ‹Γ = x:
term, α: type, A(x\α)›, where type ‹α› is fixed 🪙‹after› term ‹x›, and the
constraint ‹x :: α› is an implicit consequence of the occurrence of ‹x\α› in
the subsequent proposition.
This twist of dependencies is also accommodated by the reverse operation of
exporting results from a context: a type variable ‹α› is considered fixed as
long as it occurs in some fixed term variable of the context. For example,
exporting ‹x: term, α: type ⊨ x\α ≡ x\α› produces in the first step ‹x: term ⊨ x\α ≡ x\α› for fixed ‹α›, and only in the second step ‹⊨ ?x?\α ≡ ?x?\α›
for schematic ‹?x› and ‹?α›. The following Isar source text illustrates this
scenario. ›
notepad begin
{ fix x ― ‹all potential occurrences of some ‹x::τ› are fixed›
{ have"x::'a ≡ x" ― ‹implicit type assignment by concrete occurrence› by (rule reflexive)
} thm this ― ‹result still with fixed type ‹'a››
} thm this ― ‹fully general result for arbitrary ‹?x::?'a›› end
text‹
The Isabelle/Isar proof context manages the details of term vs.\ type
variables, with high-level principles for moving the frontier between fixed
and schematic variables.
The ‹add_fixes› operation explicitly declares fixed variables; the ‹declare_term› operation absorbs a term into a context by fixing new type
variables and adding syntactic constraints.
The ‹export› operation is able to perform the main work of generalizing term
and type variables as sketched above, assuming that fixing variables and
terms have been declared properly.
There ‹import› operation makes a generalized fact a genuine part of the
context, by inventing fixed variables for the schematic ones. The effect can
be reversed by using ‹export› later, potentially with an extended context;
the result is equivalent to the original modulo renaming of schematic
variables.
The ‹focus› operation provides a variant of ‹import› for nested propositions
(with explicit quantification): ‹∧x1… xn. B(x1, …, xn)› is decomposed
by inventing fixed variables ‹x1, …, xn› for the body. ›
text %mlref ‹ \begin{mldecls}
@{define_ML Variable.add_fixes: "
string list -> Proof.context -> string list * Proof.context"} \\
@{define_ML Variable.variant_fixes: "
string list -> Proof.context -> string list * Proof.context"} \\
@{define_ML Variable.declare_term: "term -> Proof.context -> Proof.context"} \\
@{define_ML Variable.declare_constraints: "term -> Proof.context -> Proof.context"} \\
@{define_ML Variable.export: "Proof.context -> Proof.context -> thm list -> thm list"} \\
@{define_ML Variable.polymorphic: "Proof.context -> term list -> term list"} \\
@{define_ML Variable.import: "bool -> thm list -> Proof.context ->
((ctyp TVars.table * cterm Vars.table) * thm list)
* Proof.context"} \\
@{define_ML Variable.focus: "binding list option -> term -> Proof.context ->
((string * (string * typ)) list * term) * Proof.context"} \\ \end{mldecls}
🪙🪙‹Variable.add_fixes›~‹xs ctxt› fixes term variables ‹xs›, returning
the resulting internal names. By default, the internal representation
coincides with the external one, which also means that the given variables
must not be fixed already. There is a different policy within a local proof
body: the given names are just hints for newly invented Skolem variables.
🪙🪙‹Variable.variant_fixes› is similar to 🪙‹Variable.add_fixes›, but
always produces fresh variants of the given names.
🪙🪙‹Variable.declare_term›~‹t ctxt› declares term ‹t› to belong to the
context. This automatically fixes new type variables, but not term
variables. Syntactic constraints for type and term variables are declared
uniformly, though.
🪙🪙‹Variable.declare_constraints›~‹t ctxt› declares syntactic constraints
from term ‹t›, without making it part of the context yet.
🪙🪙‹Variable.export›~‹inner outer thms› generalizes fixed type and term
variables in ‹thms› according to the difference of the ‹inner› and ‹outer›
context, following the principles sketched above.
🪙🪙‹Variable.polymorphic›~‹ctxt ts› generalizes type variables in ‹ts› as
far as possible, even those occurring in fixed term variables. The default
policy of type-inference is to fix newly introduced type variables, which is
essentially reversed with 🪙‹Variable.polymorphic›: here the given terms
are detached from the context as far as possible.
🪙🪙‹Variable.import›~‹open thms ctxt› invents fixed type and term
variables for the schematic ones occurring in ‹thms›. The ‹open› flag
indicates whether the fixed names should be accessible to the user,
otherwise newly introduced names are marked as ``internal''
(\secref{sec:names}).
🪙🪙‹Variable.focus›~‹bindings B› decomposes the outermost ‹∧› prefix of
proposition ‹B›, using the given name bindings. ›
text %mlex ‹
The following example shows how to work with fixed term and type parameters
and with type-inference. ›
ML_val ‹
(*static compile-time context -- for testing only*)
val ctxt0 = 🍋;
(*locally fixed parameters -- no type assignment yet*)
val ([x, y], ctxt1) = ctxt0 |> Variable.add_fixes ["x", "y"];
(*t1: most general fixed type; t1': most general arbitrary type*)
val t1 = Syntax.read_term ctxt1 "x";
val t1' = singleton (Variable.polymorphic ctxt1) t1;
(*term u enforces specific type assignment*)
val u = Syntax.read_term ctxt1 "(x::nat) ≡ y";
(*official declaration of u -- propagates constraints etc.*)
val ctxt2 = ctxt1 |> Variable.declare_term u;
val t2 = Syntax.read_term ctxt2 "x"; (*x::nat is enforced*) ›
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.