The following gives an overview for the most important objects passed to rules and the methods they provide. These are:
checking.Solver
for recursive checking calls, simplifying terms, inferring types etc. and is described in more detail below.checking.History
documenting the solver’s actions. A reduced version of this object is presented to the user if a judgment fails. The command history += s
extends the current history by a new string s
, history.branch
creates a new copy of the current history to pass on to recursive checks, history+s
creates a new copy and appends line s
. If the top most judgment check fails, the user is presented with those histories that lead to an error; the others are thrown away. Consequently, it is advised to branch the history for recursive checks to reduce the user output to only the relevant parts.objects.Stack
representing the current local context. It merely wraps around a Context (described here). A stack can be extended by a objects.VarDecl
(described here) v
by letting stack ++ v
.Both the stack and the history are usually implicit arguments.
It is strongly advised to always import objects.Conversions._
, which provides helpful syntactic sugar for handling common operations such as substituting variables.
Context.pickFresh(context,name)
picks a fresh variable name newname
(by default name
) that is not already used in context
, and returns it along with the objects.Substitution
name/OMV(newname)
.api.LocalName
ln
and a term a
, then ln % a
returns the VarDecl
with name ln
and type a
.ln
and a term a
, then ln / a
returns the substitution ln/a
.a
and a substitution s
, then a ^? s
returns the result of applying s
to a
stack ++ ln % (tp ^? (x / a))
The checking.Solver
provides the following methods for rules:
solver.check
is the main method for recursive checks of rule premises. Takes a judgement (see here) as argument. Note that a call to solver.check(j)
implies that the judgement j
is a necessary requirement for the current judgement. In particular, failure to prove j results in an error.solver.tryToCheckWithoutDelay(j : Judgement*)
: Like solver.check
, but without side effects. In particular, no unsolved variables are solved unless the judgements j
are provable.solver.inferType(tm : Term, covered : Boolean)
attempts to infer the type of tm
. The additional argument covered
denotes whether the term is already known to be well-typed, in which case some well-formedness checks can be skipped.solver.inferTypeAndThen(tm : Term)(cont : Term => Boolean)
is useful for rules that return Booleans. If a type for tm
can not be inferred yet due to unsolved variables, the current judgement will be delayed until the variables are solved during further checks. Afterwards, the continuation function will be called on the inferred type to check the current judgement.solver.simplify(o : Obj)
aggressively simplifies o
until all computation rules are exhausted.solver.safeSimplifyUntil(tm : Term)(simple : Term => Option[A])
safely iterates one-step-simplification until simple
(usually an unapply-method) returns Some
on the result. This is primarily used to enforce a specific syntactic pattern on a term, if a rule requires a term to be e.g. a lambda expression (solver.safeSimplifyUntil(tm)(Lambda.unapply)
). Returns both the simplified term as well as the result of simple
on the result.solver.getType
and solver.getDef
take a GlobalName
(see here) for a constant and attempt to return its type or definiens (relative to the current context), respectively.solver.error(s : String)
should always be used instead of returning false
. This method a) returns false
and b) registers s
as the reason why the judgement failed, thus providing more detailed information to the user.