next up previous contents
Next: 6 Printing Up: Elf Users Guide Previous: 4 Fixity and Name

5 Term Reconstruction

The model of term reconstruction employed by Elf is straightforward, although it employs a relatively complex algorithm. ``Term reconstruction'' includes type reconstruction in the usual sense plus the reconstruction of implicit arguments to functions and filling in wildcards (written as an underscore _ in the input). The basic principle to keep in mind is the duality between omitted quantifiers and implicit arguments. For example,

o : type.
& : o -> o -> o.  %infix right 5 &
pf : o -> type.
andi : pf A -> pf B -> pf (A & B).

The last declaration will be printed back as one of the following two

andi : {A:o} {B:o} pf A -> pf B -> pf (A & B).
andi : {B:o} {A:o} pf A -> pf B -> pf (A & B).

which is the internal representation. Note that the quantifiers on A and B were omitted in the signature. This means that the corresponding arguments to andi remain implicit and will be reconstructed. For example,

([p:o] [u:pf p] andi u u) : {p:o} pf p -> pf (p & p).
is parsed into
([p:o] [u:pf p] andi _ _ u u) : {p:o} pf p -> pf (p & p).
and the two wildcards are determined to be p. Note that the second line is not a legal input, since the implicit arguments must be omitted. This is a necessary consequence of Elf's approach to term reconstruction, since the order of the implicit quantifiers (and therefore of the implicit arguments) is unspecified in the declaration of andi above.

Wildcards can also be explicitly given or they arise from implicit types in abstractions [x] or quantifiers x. For example,

([p] [u:pf p] andi u u) : {p} pf p -> pf (p & p).
is parsed into
([p:_] [u:pf p] andi _ _ u u) : {p:_} pf p -> pf (p & p).
and the values for the wildcards are determined as o, p, p, and o, respectively.

The algorithm for reconstruction employs an algorithm for solving constraints over dependently typed lambda-terms described in a paper ``Unification and Anti-Unification in the Calculus of Constructions,'' LICS'91. Effectively, the value of an omitted argument may be determined from other arguments, or synthesized from the context in which an expression occurs. Here are some typical examples:

v : o -> o -> o.  %infix right 4 v
ori : pf A -> pf (A v B).

In an expression (ori M) which is parsed into (ori _ _ M), the value of the first argument for, say, A, can be determined from M. However, the value of B can only be determined from the context it appears in. For example,

([p:o] [u:pf p] ori u) : {p:o} pf p -> pf (p v p).
determines both implicit arguments to ori as p. We refer to this situation by saying that in the declaration of ori, A can be synthesized, and both A and B can be inherited.

In some circumstances, an argument can neither be synthesized nor inherited. For example, consider the additional declarations

i : type.
all : (i -> o) -> o.
alle : pf (all A) -> pf (A T).

Now T can not always be inherited (and clearly not synthesized). For example,

([p:o] [u:pf (all [x:i] p)] alle u) : {p:o} pf (all [x:i] p) -> pf p.
but the implicit argument T to alle is inherently ambiguous (could be any term). Such a situation often leads to trouble during term reconstruction later on and is flagged with a warning by the front-end. In such a situation, it is desirable to make the argument explicit, as in
alle : {T:i} pf (all A) -> pf (A T).
Note that A can be synthesized and can therefore safely remain implicit.

In some circumstances it is useful to directly ascribe a type in order to disambiguate inherited arguments. For example

[p:o] [u:pf p] ori p
has type {p:o} pf p -> pf (p v A p) for any A : o -> o. We can disambiguate by ascribing a type to a subexpression. For example,
[p:o] [u:pf p] (ori p : pf (p v p))

In some circumstances, especially when arguments of some constants can be neither synthesized or inherited, a principal reconstruction for a given declaration does not exist even for expressions which have some type. In such a case the front-end issues an error message about remaining constraints after type reconstruction. The constraints are shown, but they are often difficult to interpret. As a general rule of thumb, (1) arguments which can be neither synthesized nor inherited should be made explicit, and (2) variables which occur only at the head of some application should be avoided. In the latter case the expression can often be disambiguated by explicitly ascribing types which mention the variable in question in a synthesizable or inheritable position.

When term reconstruction fails, the front-end issues an error message with the line number of the declaration in which the problem occurred and the disagreement encountered, printed in the form

<filename>:<location> Error:
Type checking failed on declaration of c
M : A <> B
Unification failure due to <reason>

which means that the type inferred for M was A but that some constraints required it to be equal to B, and A and B are different (that is, do not unify). The reason why A and B do not unify is given in the next line. The filename and location information can be used by Elf's Emacs interface to jump to the specified location in the given file for editing of the incorrect declaration for the constant c. The location has the form line1.column1-line2.column2 and represent Elf's best guess as to the source of the error. Due to the propagation of non-trivial constraints the source of a type reconstruction failure can sometimes not be pinpointed. In that case the error message has the form

<filename>:<location> Error:
Type checking failed on declaration of c
M : A 
is inconsistent with other constraints
Unification failure due to <reason>

When the reason for failure of type reconstruction is elusive, a recommended strategy is to explicitly ascribe types to subterms occurring in the declaration, using the form (M : A). It is also possible to trace type reconstruction; this feature will be made available in the Elf server at some future time.

Normally, error and trace messages are printed in reparsable format, but sometimes this is impossible--the printer then falls back on internal form and surrounds every externally unprintable term in double quotes.



next up previous contents
Next: 6 Printing Up: Elf Users Guide Previous: 4 Fixity and Name



fp@cs