The source can be downloaded here:
http://people.csail.mit.edu/garland/LP/
It had FiniteMap object type:
FiniteMap (M, D, R): trait
% An M is a map from D's to R's.
introduces
{}: -> M
update: M, D, R -> M
apply: M, D -> R
defined: M, D -> Bool
asserts
M generated by {}, update
M partitioned by apply, defined
forall m: M, d, d1, d2: D, r: R
apply(update(m, d2, r), d1) ==
if d1 = d2 then r else apply(m, d1);
~defined({}, d);
defined(update(m, d2, r), d1) ==
d1 = d2 \/ defined(m, d1)
implies
Array1 (update for assign, apply for __[__],
M for A, D for I, R for E)
converts apply, defined
exempting forall d: D apply({}, d)
http://nms.lcs.mit.edu/Larch/handbook/FiniteMap.html
Basically a type generated from {} and update, whereby
{} being the empty function. Besides an apply function
it has also a defined predicate. The functions itself are
conceived partial. The apply might react like Dan-O-Matiks
function when outside domain, in that apply(m, d) has
neither provable the value a nor the value b, but nevertheless
the underlying simple typed language would make apply ?total?
on the full D. On the other hand defined is more precise, we
even have ~defined({}, d) provable. So I guess one would use
defined and not apply, in case one is interested which subset of
D corresponds to the domain of a give function. If the type is
not inductive from {} and update, it would also allow infinite
functions and their updates.