Simply Typed Lambda-Calculus with a small-step, allocation semantics. 
Matthew Fluet (June 2005)

This is an attempt to get a feel for Twelf with a problem somewhere
between the trivial simply typed lambda-calculus with a large-step
semantics (a Twelf documentation example) and Karl Crary's TALT
formalization (which is too complex to be useful for a novice).

Ultimately, we'd like to have a formalization of URAL and it's
successors.  While Greg has worked out a seemingly adequate encoding
of the core URAL type system, it remains unclear precisely how one
will invert the "resp_qual" predicate to reason about the use of
variables and values.  This is compounded by the fact that the core
URAL dynamic semantics is agnostic with respect to the substructural
qualifiers; hence, the type safety proof need never appeal to the a
qualifier to justify a preservation or progress argument.

refURAL ameliorates this deficiency somewhat, since the dynamic
semantics will (obviously) get stuck trying to read/write/swap/free a
unique reference after it has been freed.  Less obviously, the dynamic
semantics will also get stuck if a unique reference were to be
duplicated, strongly updated with one copy, and used at the old type
with the other copy.  As pointed out by the ICFP reviewers (and
pending revision into the ICFP paper), one can further instrument the
heap and heap typing so that unreachable (and, hence, manifestly
unused) linear and relevant references are a typing error.

However, refURAL is a bit too far down at the deep end of the pool for
me to jump with when I can barely swim in Twelf.  One can also
ameliorate the deciciency with core URAL's dynamic semantics by
adopting an allocation semantics that deallocates linear (and affine)
values at their uses in elimination forms.  This is Walker's approach
in ATTAPL.  Presumably, one can also instrument the store and store
typing so that unreachable linear (and relevant) values are a typing
error.  Such an immutable (modulo deallocation) store has the
attractive property that it is essentially inductive, since new values
can only point to older values, and should be somewhat easier to
formalize than a mutable heap.

Still, the combination of a substructural type system and an
allocation semantics would put me in over my head.  So, witness the
following, a type safety proof for the simply typed lambda calculus
(with unit and pairs), whose dynamic semantics is given by a
small-step, allocation semantics.

I've tried to keep the formalism as simple as possibly, but there are
a few aspects of the proof with which I am a little unhappy.


* Store Representation

As was noted on the POPLMark mailing list, modeling an abstract
machine with a store, considered as a partial map from (alpha-varying)
labels to values (which may have free occurences of those labels), is
very awkward to encode.  Karl Crary reported that it is possible to
pursue an approach where all the binders (labels) are introduced
first, then lay out the store, using a dependent type to ensure that
the number of binders equals the number of entries in the heap.
However, he found it too annoyig and resorted to non-alpha-varying
labels.

With that in mind, I've adopted the second approach, cribbing a little
from what I saw in Karl's TALT formalization.  Essentially, a store is
a list of (location, value) pairs, where locations are isomorphic to
the natural numbers:

st        : type.   %name st S.         %% store.elf
nil_st         : st.
cons_st        : loc -> val -> st -> st.

A store typing is analagous: a list of (location, type) pairs.

One normally wishes to ensure that a store does not contain
duplicate entries for the same location.  A simple means of
maintaining this invariant is to ensure that the locations in the
store are kept in sorted order, and to ensure that new locations are
greater than any current location.  I capture this invariant with a
store well-formedness judgement:

st_wf          : st -> type.
st_wf_nil           : st_wf nil_st.
st_wf_cons_nil      : st_wf (cons_st L V nil_st).
st_wf_cons_cons     : st_wf (cons_st L' V' S') ->
		      loc_lt L' L
		       -> st_wf (cons_st L V (cons_st L' V' S')).

It occured to me that one could (almost) equally well take this as
_the_ definition of a store.  And, for a time, I attempted to proceed
by always introducing a store with a proof of its well-formedness.
However, this became somewhat cumbersome, and there were relatively
few situations where I really needed the well-formedness requirement.
Also, note that induction on stores requires two cases, while
induction on store well-formedness proofs requires three cases; hence,
where it is possibly to get by with an arbitrary store, it is
expedient to do so.

Store allocation is represented by a relation:

st_alloc       : st -> val -> st -> loc -> type.
%mode st_alloc +S1 +V -S2 -L.

where, for convenience, the freshly chosen location is an output,
along with the extended store.  Location 0 is allocated when the input
store is empty; otherwise, location N+1 is allocated when the head of
the input store is location N.  A (proved) lemma is that store
allocation preserves store well-formedness ("st!alloc_preserves_wf").


* Machine Representation (or, lack thereof)

I would have liked to define a machine as a (store, expression) pair.

mach      : type.   %name mach M.
;              : st -> exp -> mach.               %infix right 20 ;.

This would have led to an elegant definition of the small-step,
allocation semantics:

==>            : mach -> mach -> type.            %infix right 10 ==>.
%mode ==> +M1 -M2.

lam_alloc_step : st_alloc S (lam_v F) S' L'
                  -> (S ; (lam_e F)) ==> (S' ; (loc_e L')).
app_ctxt1_step : (S ; E1) ==> (S' ; E1')
                  -> (S ; (app_e E1 E2)) ==> (S' ; (app_e E1' E2)).
app_ctxt2_step : (S ; E2) ==> (S' ; E2')
                  -> (S ; (app_e (loc_e L1) E2)) ==> (S' ; (app_e (loc_e L1) E2')).
app_beta_step  : st_lookup S L1 (lam_v F1')
                  -> (S ; (app_e (loc_e L1) (loc_e L2))) ==> (S' ; (F1' (loc_e L2))).
...

A natural property to expect and to check is the termination of "==>"
as logic program from input machines to output machines.  Intuitively,
termination (of the single step relation) holds because the expression
in the machine is smaller at each recursive call.  However, if I
present Twelf with the termination declaration:

%terminates M (==> M _).

it responds with the error:

Termination violation:
 ---> (S ; E1) < (S ; app E1 E2)

I understand that (S ; E1) is not a strict subterm of (S ; app E1 E2),
but it does seem that (S ; E1) is smaller than (S ; app E1 E2).  I was
unable to find any way to convey my intuition to Twelf.

There is an obvious way to avoid this: declare a four-place relation:

step           : st -> exp -> st -> exp -> type.
%mode step +S1 +E1 -S2 -E2.
...
%terminates E1 (step _ E1 _ _).

While I think of a machine as a distinct, if degenerate, syntactic
object, and would like to reflect that into the formalism in Twelf, it
is certainly not required.  I note that in TAPL's chapter on
references, Pierce does not define a typing judgement for machines of
the form |- (S, E) : (ST, T) or |- (S, E) (where the store typing and
expression type are left existentially bound).  Trying to prove
progress by induction on judgements of the form |- (S, E) runs afoul
of the same issue as above, where one wants to take |- (S, E1) as
smaller than, though not strictly a subterm of, |- (S, app_e E1 E2).


* Lambdas

I had a hard time settling on a definition of lambda expressions and
the typing judgement for lambda expressions.  I finally settled upon
the following:

exp       : type.   %name exp E.        %% exp.elf
lam_e          : (exp -> exp) -> exp.

|-var          : exp -> tp -> type.
%block |-var_block : some {T:tp} block {x:exp} {_:|-var x T}.
%worlds (|-var_block) (|-var _ _).

|-exp          : sttp -> exp -> tp -> type.
|-exp_var      : |-var E T
		  -> |-exp ST E T.
|-exp_lam      : ({x} |-var x T1 -> |-exp ST (F x) T2)
		  -> |-exp ST (lam_e F) (fn_t T1 T2).
%worlds (|-var_block) (|-exp _ _ _).

I originally tried to make headway with lambda expressions defined by:

lam_e          : (loc -> exp) -> exp.

|-loc          : sttp -> loc -> tp -> type.
%block |-loc_block : some {ST:sttp} {T:tp} block {l:loc} {_:|-loc ST l T}.
|-loc_         : sttp_lookup ST L T
		  -> |-loc ST L T.
%worlds (|-loc_block) (|-loc _ _ _).
|-exp_lam      : ({l} |-loc ST l T1 -> |-exp ST (F x) T2)
		  -> |-exp ST (lam_e F) (fn_t T1 T2).
%worlds (|-loc_block) (|-exp _ _ _).

which I had a vague hope would make it manifest that the argument to a
function must be evaluated to an allocated location before being
substituted.  Unfortunately, I seemed to be required to propagate
"|-loc_block" worlds through more relations than necessary.  I also
think that this approach would have run into problems similar to the
one described in the next few paragraphs.

Having revised the definition of lambda expressions to 

lam_e          : (exp -> exp) -> exp.

I attempted to define the typing judgement for lamba expressions as
the following:

|-exp          : sttp -> exp -> tp -> type.
%block |-exp_block : some {ST:sttp} {T:tp} block {x:exp} {_:|-exp ST x T}.
|-exp_lam      : ({x} |-exp ST x T1 -> |-exp ST (F x) T2)
		  -> |-exp ST (lam_e F) (fn_t T1 T2).
%worlds (|-exp_block) (|-exp _ _ _).

This worked quite well for a time, but I ultimately ran afoul of the
'Worlds and Weakening' problem described on the Twelf Wiki
(http://fp.logosphere.cs.cmu.edu/twelf/?n=Answers.WorldsAndWeakening).
I don't pretend to completely understand the issue, nor the solution.
Like Rob Simmons, the problem arose with coverage checking of the
weakening lemma ("sttp_weak_|-exp").  Adopting the solution given in
the wiki, I was led to introduce the "|-var" relation (with no
canonical elements) and the "|-var_block" declaration.  Now that there
are no store typings in the hypothesis of the typing judgement of the
lambda expression, the weakening lemma goes through with no problem.

But, by the conservation of grief principle, another problem arises
with the Preservation Lemma.  Specifically, the case for "app-beta"
requires the substitution of a location (expression) for the formal
argument of a lambda expression to preserve the type of the
application expression; i.e., to preserve the type of the body of the
lambda expression.  Of course, one cannot simply apply the hypothesis
of the "|-exp_lam" judgement to the argument 
("|-exp ST (loc_e L2) Tx"), since the hypothesis is phrased in terms
of "{x:exp} |-var x T".  Following the discussion on the wiki, I
simply prove the lemma that asserts that such a substitution is type
preserving:

pres_var       : ({z} |-var z Tz -> |-exp ST (F z) T) ->
                 |-exp ST E Tz
		  -> |-exp ST (F E) T -> type.

What is disappointing is that the statement and proof of the lemma has
precisely the feel of a substitution lemma, something that I thought
one avoided with higher-order abstract syntax.

In any case, the wiki also suggests that there may be limitations in
this approach, so I am curious how the solution alluded to by Susmit
Sarkar actually proceeds.

One final note about lambda expressions.  Note that I have given the
lambda expressions in 'Curry-style', with no type annotation on the
'argument'.  One would expect that adopting 'Church-style' would be a
simple syntactic change.  However, one interesting situation arises.
Coverage checking of the Preservation Lemma fails, reporting the
following missing case (slightly editted for presentation):

   {S1:st} {L1:loc} {L2:loc} {F1:exp -> exp} {ST1:sttp} {ST2:sttp} 
   {T1:tp} {T2:tp} {T3:tp}
   {X1:st_lookup S1 L1 (lam_v T2 F1)} 
   {X2:st_wf S1}
   {X3:sttp_wf ST1} 
   {DStTc1:|-st S1 ST1} 
   {X4:sttp_lookup ST1 L1 (fn_t T3 T1)}
   {X5:sttp_lookup ST1 L2 T3} 
   {X6:sttp_extend ST1 ST2}
   {DStTcWf1:|-stwf S1 ST2} 
   {DExpTc1:|-exp ST2 (F1 (loc_e L2)) T1}
   |- pres (app_beta_step X1) 
           (|-stwf_ X2 X3 DStTc1)
           (|-exp_app (|-exp_loc (|-loc_ X4)) (|-exp_loc (|-loc_ X5))) 
           X6 
           DStTcWf1
           DExpTc1.

The interesting assumptions are "X1" and "X4".  It appears that Twelf
is concerned about a situation where the lookup of a location in the
store yields a lambda value with argument type annotation T2, while
the lookup of the same location in the store typing yields a function
type with argument type T3.  This situation should be ruled out by
assumption "DStTc1", namely, that the store is typed by the store
typing.  Presumably an appropriate 'reductio ad absurdum' can be
applied, though I was somewhat suprised by how the missing case was
isolated to lambda expressions.

%% Follow-up.  Randy Pollack recently asked on the Twelf-list about
this very situation.  Karl Crary responded, noting that the solution
is to provide an inversion lemma for the "|-val_lam" rule:
http://www.twelf.org/pipermail/twelf-list/2005-May/000046.html
I've incorporated this solution into the proof.


* Inversion

One final item of note is that I ended up stating and proving two
lemmas related to the inversion of the store typing judgement.  What I
found interesting is that the LF types of these lemmas are identical,
only their modes differ:

pres_|-st_inversion 
               : |-stwf S ST ->
                 sttp_lookup ST L T ->
                 st_lookup S L V 
                  -> |-val ST V T -> type.
%mode pres_|-st_inversion +DStTcWf +SttpLookup +StLookup -DValTc.
prog_|-st_inversion 
               : |-stwf S ST ->
                 sttp_lookup ST L T 
                  -> st_lookup S L V ->
                     |-val ST V T -> type.
%mode prog_|-st_inversion +DStTcWf +SttpLookup -StLookup -DValTc.

In the proof of Preservation, I already have the assumption that the
store lookup succeeds with a value, so inversion yields the fact that
the value has the type ascribed to that location in the store typing.
In the proof of Progress, I only have the assumption that the store
typing lookup suceeds with a type, so inversion yields the fact that
the store lookup will succeed with a value of the appropriate type.

Technically, the "prog_|-st_inversion" lemma could be obtained by
composing "pres_|-st_inversion" and a simpler lemma that yields the
store lookup from the store typing lookup.  However, this 'simpler'
would have exactly the same structure as "prog_|-st_inversion", simply
discarding the value typing which is manifestly available.

The "pres_|-st_inversion" lemma is interesting as it is the one place
where I needed a 'reductio ad absurdum' argument, to rule out the
cases where the store typing lookup hits but the store lookup misses,
where the store typing lookup misses but the store lookup hits, and
where both the store typing lookup misses and the store lookup misses
but with different head locations.  Ultimately, the contradiction
always reduces to an (absurd) assumption of the form "loc_lt L L".

Also, the store and store typing well-formedness judgements are used
in the inversion lemmas to prove that a non-empty store or store
typing is an extension of its tail.


* Adequacy

I've seen the definition of 'adequate representation' enough to have
an intuition about what it is trying to establish.  But, like Twelf
proofs in general, I haven't found many examples in that are useful.
Most, like the simply typed lamdba calculus or natural deduction, are
so transparently adequate that they are ultimately useless.

While I am fairly confident that the encoding here is adequate, I
can't honestly say I know how to go about verifying that claim.  That
said, there are a couple of things of which I am slightly suspicious;
at the very least, I would like to hear an explaination of why they
don't cause any problems.

One thing concerns the "|-var" relation above.  I note that for the
'reductio ad absurdum' arguments in Twelf, one introduces an "absurd"
type with no canonical elements, lemmas that derive "absurd" from
contradictory assumptions, and lemmas that derive anything from an
"absurd" assumption.  What is slightly troublesome is the fact that
the "|-var" relation has no canonical elements, and, hence, one might
consider it isomorphic to "absurd".  Which makes the "|-exp_lam" rule

|-exp_lam      : ({x} |-var x T1 -> |-exp ST (F x) T2)
		  -> |-exp ST (lam_e F) (fn_t T1 T2).

appear to introduce a false assumption in the hypothesis.  Greg
suggests that the reason this is not problematic is the fact that the
"|-exp" relation is defined with a "|-var_block" world.  Hence, we are
allowing "|-var" as an allowable assumption, even in the absence of
any canonical elements.  But, I can't say that puts all my concerns to
rest.

Another related issue.  The vacuousity of the "|-var" relation
'breaks' coverage checking of the "|-exp" relation, in the sense that
I can leave out a typing rule for an expression form and coverage
checking succeeds.  Certainly I don't need coverage of "|-exp" to
establish the safety of the language, but it is something that I'm
naturally led to add to the declarations of the typing rules and it is
a bit disconcerting that it no longer means what I thought it meant.


* Minor Twelfisms

I don't know how often it comes up in practice, but one thing I found
annoying was the lack of any form of local term abbreviations.  For
example, here is the definition for store allocation on a non-empty
store:

st_alloc_cons  : st_alloc (cons_st (mkLoc N) V S) 
		          V' 
		          (cons_st (mkLoc (s_nat N)) V' (cons_st (mkLoc N) V S)) 
		          (mkLoc (s_nat N)).

I feel that there is a lot of repetition that obscures things.  I was
looking for some sort of syntax like:

st_alloc_cons  : let L = mkLoc N in
                 let S = cons_st L V _ in
                 let L' = mkLoc (s_nat N) in
                 let S' = cons_st L' V' S in
                 st_alloc S V' S' L'.

which I felt would make the definition clearer.

Another annoyance is the lack of actual functions, particularly on
types with a single canonical form.  For example, in the
"st_alloc_cons" definition above, I need to unwrap a location,
exposing the natural, increment the natural, and wrap back to a
location.  Whereas, knowing that "loc" has the single canonical
element

mkLoc          : nat -> loc.

it seems clear that I could write function "inc_loc" with the same
behavior, which would further simplify the definition above.  Perhaps
Carsten Schürmann's current work on Delphin is working in this
direction.

I know I could define "inc_loc" as a 'function moded' relation:

inc_loc        : loc -> loc -> type.
%mode inc_loc +L1 -L2.
inc_loc_       : inc_loc (mkLoc N) (mkLoc (s_nat N)).

But then even my 'improved' "st_alloc_cons" definition would look
like:

st_alloc_cons  : let S = cons_st L V _ in
                 let S' = cons_st L' V' S in
                 inc_loc L L'
                  -> st_alloc S V' S' L'.

which still seems overly complicated, as it introduces a hypothesis
which in turn has a single (total) cannonical element.


* Theorem Prover

I was pleasantly suprised to see that the theorem prover was able to
prove a number of lemmas that I had first proved by hand.  The theorem
prover ultimately balked at the store typing weakening lemma for
"|-val".  What is somewhat odd is that the thorem prover succeeds
without complaint on the corresponding lemma for "|-exp", and the hand
proof for "|-val" is a straightforward proof by cases.  I suppose the
subtlety that foils the theorem prover is that the case for a lambda
value requires appealing to the weaking of "|-exp" under the lambda.

The disappointing aspect of the theorem prover is that it seems that
proven theorems are not available as relations for use in by hand
proofs.  (This is a consequence of the fact that proofs are not
presently realized as logic programs?)  It would be very nice to use
the theorem prover for trivial lemmas, but be able to appeal to those
lemmas in theorems that are not provable by the theorem prover.


* Conclusion

I'm interested in hearing anyones thoughts or opinions.  I'm hoping to
add some more comments to the formalism, making it a good exemplar.
Also in that vein, I'd like to send it off to some Twelf experts for
some constructive criticism.  I also want to read through the POPLMark
Challenge solution with the insights I've gained from this endeavor.
I'm certain that it will make more sense now, though at a first
reading, it was a bit deeper than I was comfortable swimming.
