You are on page 1of 14

A Language with Distributed Scope

Luca Cardelli
Digital Equipment Corporation, Systems Research Center
130 Lytton Ave, Palo Alto, CA 94301, USA
luca@src.dec.com

Abstract migration, in section 4.6. The syntax is summarized in


the appendix.
Obliq is a lexically-scoped, untyped, interpreted lan-
guage that supports distributed object-oriented 1.1 Language Overview
computation. Obliq objects have state and are local to a
site. Obliq computations can roam over the network, The principal way of structuring distributed
while maintaining network connections. Distributed computations in Obliq is through the notion of objects.
lexical scoping is the key mechanism for managing Network services normally accept a variety of
distributed computations. messages; it is then natural to see each service as a
network object (or, more neutrally, as a network
1. Introduction interface). Obliq supports objects in this spirit, relying
for its implementation on Modula-3Õs network objects
A simple guiding principle separates Obliq from [7].
other distributed procedural languages: adherence to The Obliq object primitives are designed to be
lexical scoping in a distributed context. This principle simple and powerful, with a coherent relationship
has a number of interesting consequences: it supports a between their local and distributed semantics. Obliq
natural and consistent semantics of distributed objects are collections of named fields, with four basic
computation, and enables elegant techniques for operations: selection/invocation, updating/overriding,
distributed programming. cloning, and aliasing. There are no class hierarchies, nor
In lexically scoped languages, the binding location of complex method-lookup strategies. Every object is
every identifier is determined by simple analysis of the potentially and transparently a network object. An
program text surrounding the identifier. Therefore, one object may become accessible over the network either
can be sure of the meaning of program identifiers, and by the mediation of a name server, or simply by being
can much more easily reason about the behavior of pro- used as the argument or result of a remote method.
grams. In a distributed language like Obliq, lexical In any framework where objects are distributed
scoping assumes a further role. It ensures that across sites, it is critical to decide what to do about
computations have a precise meaning even when they mobility of state. To avoid problems with state
migrate over the network: a meaning that is determined duplication, objects in Obliq are local to a site and are
by the binding location and network site of identifiers, never automatically moved over the network. In
and not by execution sites. contrast, network references to objects can be transmitted
Network-wide scoping becomes an issue in the pres- from site to site without restrictions. Atomic object
ence of higher-order distributed computation, for migration can be coded from our primitives,
example when a site acting as a compute server accepts specifically from cloning and aliasing.
procedures for execution. The question here is: what In addition to the distribution of data, the
happens to the free identifiers of network-transmitted distribution of computations must also be designed
procedures? Obliq takes the view that such identifiers carefully. It is clearly desirable to be able to transmit
are bound to their original locations, as prescribed by computing agents for remote execution. However, one
lexical scoping, even when these locations belong to should not be satisfied with transmitting just the
different network sites. program text of such agents. Program text cannot carry
In the rest of this introduction we review the main with it live connections to its originating site, nor to any
notions. In section 2 we describe ObliqÕs object model data or service at any other site. Hence the process of
and distributed semantics. In section 3 we illustrate the transmitting program text over the network implies a
object model by means of single-threaded examples. In complete network disconnect from the current
section 4 we present a collection of distributed distributed computation. In addition, unpredictable
programming techniques, enabled by ObliqÕs unique dynamic scoping results from transmitting and then
features. The most illuminating example is the compute running program text containing free identifiers.
server, in section 4.2; the most intriguing one is object

May 30, 1995 9:22 PM Page 1


Obliq computations, in the form of procedures or variables in its program text that refer to locations in
methods, can be freely transmitted over the network. the surrounding lexical scope.
Actual computations (closures, not source text) are Values may be transmitted over the network. A value
transmitted; lexically scoped free identifiers retain their containing no embedded locations is copied on
bindings to the originating sites. Through these free transmission. A value containing embedded locations is
identifiers, migrating computations can maintain copied up to the point where those locations appear;
connections to objects and locations residing at various local references to locations are replaced by network
sites. Disconnected agents can be represented as references. Because of transmission, a value may thus
procedures with no free identifiers; they do not rely on contain network references to locations at different
prolonged network connectivity. sites. This semantics of value transmission has
In order to concentrate on distributed computation particular implications for closure values.
issues and to reduce complexity, Obliq is designed as In general terms, a closure is a pair consisting of a
an untyped language (lacking static typing). This piece of source text and a pointer to an evaluation
decision leads to simpler and smaller language stack. Transmission of a closure, in this view, implies
processors that can be easily embedded in applications. transmission of an entire evaluation stack. Obliq,
Moreover, untyped programs are somewhat easier to however, implements each closure as a pair of a source
distribute, avoiding problems of compatibility of types text and a table of values for free identifiers; this
at multiple sites. technique is well-known and applicable to lexically-
The Obliq run-time, however, is strongly typed: erro- scoped higher-order languages. In our context, this
neous computations produce clean errors that are cor- implementation of closures has the effect of reducing
rectly propagated across sites. The run-time data space network traffic by transmitting only the values from the
is heterogeneous, meaning that there are different kinds evaluation stack that are needed by the closure. A
of run-time values and no provisions to discriminate closure that has been transmitted may thus contain
between them; heterogeneity discourages writing program text that, when executed, accesses remote
programs that would be difficult to typecheck in typed locations (via its table of free identifiers) over the
languages. network.
Because of heterogeneity and lexical scoping, Obliq Every Obliq object consists of a collection of
is in principle suitable for static typing. More locations spanning a single site; hence the object itself is
importantly, Obliq is compatible with the disciplined bound to a unique site, and does not move1 . This
approach to programming that is inspired by statically immobility of objects is not a strong limitation, because
typed languages. objects can be cloned to different sites, and because
procedures can be transmitted that allocate objects at
1.2 Distributed Semantics different sites. Hence, a collection of interacting objects
can be dynamically allocated throughout the network.
The Obliq distributed semantics is based on the no-
If migration is necessary, cloning can be used to
tions of sites, locations, values, and threads.
provide the needed state duplication, and aliasing can
Sites (that is, address spaces) contain locations, and
be used to redirect operations to the clones.
locations contain values. Each location belongs to a
We have stressed so far how Obliq computations can
unique site. Sites are not explicit in the syntax but are
evolve into webs of network references. However, this
implicit in the creation of locations: when a location is
is not necessarily the case. For example, a procedure
created during a computation, it is allocated at the
with no free identifiers forms a completely self-
current site.
contained computing agent. The execution of such an
Threads are virtual sequential instruction processors.
agent may be carried out autonomously by a remote
Multiple threads may be executed concurrently, both at
compute server; the agent may dynamically reconnect
the same site or at different sites. A given thread may
to the originating site to report results. Intermediate
stop executing at a site, and continue executing at
situations are also possible, as with semi-autonomous
another site. That is, threads may jump from site to site
agents that maintain low-traffic tethers to their
while retaining their conceptual identity.
originating site for status queries.
In the Obliq syntax, constant identifiers denote values,
while variable identifiers denote locations. A location
1.3 Discussion
containing a value may be updated by assignment to
the variable denoting the location. The distributed semantics of Obliq is defined so that
Obliq values include basic values (such as strings and data and computations are network-transparent: their
integers), objects, arrays, and closures (the results of meaning does not depend on allocation sites or
evaluating methods and procedures). execution sites (of course, computations may receive
A value may contain embedded locations. An array different arguments at different sites). At the same
value has embedded locations for its elements, which
can be updated. An object value has embedded 1 In the implementation, network references are generated to ob-
locations for its fields, which can be updated. A closure jects and arrays, not to each of their embedded locations.
However, it is consistent and significantly simpler to carry out
value may have embedded locations because of free our discussions in terms of network references to locations.

Page 2 May 30, 1995 9:22 PM


time, Obliq programs are network-aware: distribution methods, thus it can work locally and autonomously
is achieved by explicit acts that give full control on when it reaches its destination. In a delegation-based
communication patterns. Central to network model it would be more difficult to obtain the complete
transparency is the notion of distributed lexical relocation of an object and its methods. Typically, this
scoping. would require the coordinated migration of the objectÕs
The combination of lexical scoping with strong run- parents [33], and would affect other objects that share
time typing and interpreted execution can provide net- the same parents.
work security guarantees. Consider the situation of a
server executing incoming foreign agents. Because of 2.1 Fields
lexical scoping, these agents have access only to the
An Obliq object is a collection of fields containing
data and resources that they can reference via free
methods, aliases, or other values. A field containing a
identifiers or that they explicitly receive in the form of
method is called a method field. A field containing an
procedure parameters. Hence, foreign agents cannot
alias is called an alias field. A field containing any other
access data or resources at the server site that are not
value, including a procedure value, is called a (proper)
explicitly given to them. As a concrete example,
value field. Each field is identified by a field name.
operations on files in Obliq require file system handles
Syntactically, an object with n fields has the form:
that are provided only as global lexically-bound
identifiers at each site. A foreign agent can use the file { x1 => a1, ... ,xn => an }
system handle of its originating site, simply by
where n≥0, and x i are distinct field names. The terms
referring to it as a free identifier. But the file system
ai are siblings of each other, and the object is their host
handle at the server site is outside its lexical scope, and
object.
hence unobtainable except with the cooperation of the
A value field is, for example:
server. Degrees of file protection can be represented by
file system handles with special access rights. x => 3
In summary, distributed lexical scoping makes it
A method field has the form:
easy to spread computations over multiple network
sites, since computations are likely to behave correctly x => meth(y,y1, ... ,ym) b end
even when they are carried out at the wrong place (by
The first parameter, y, denotes self: the methodÕs host
some measure). This flexibility in distribution can,
object. The other parameters, for m≥0, are supplied
however, result in undesirable network traffic. Obliq
during method invocation. The body of the method is
relieves some of the burden of distributing data and
b, which computes the result of an invocation of x.
computations, but care and planning are still required
Methods and procedures are supported as distinct
to achieve satisfactory distributed performance.
concepts. Procedures start with the keyword proc in-
stead of meth and have otherwise the same syntax. The
2. Objects main differences between the two are as follows.
Obliq is an object-oriented language based on Methods can be manipulated as values but can be
objects, rather than classes. An object is a self-contained activated only when contained in objects, since self
exemplar of behavior that can be either constructed needs to be bound to the host object. In contrast,
directly or cloned from other objects. The Obliq procedures can be activated by normal procedure call.
language is therefore prototype-based [10], but is not Further, a procedure can be inserted in a value field and
delegation-based [22]. Obliq belongs to a category of later recovered, while any attempt to extract a method
prototype-based languages that we may call embedding- from an object results in its activation.
based2 [31]. This name indicates that all the methods of An alias field has the form:
an object, as well as its value fields, are embedded in x => alias y of b end
the object itself (at least in principle) rather than being
located in other objects or classes. In spirit, this model is Operations on the x field of this object are redirected to
close to BorningÕs original prototype-based proposal the y field of the object b. If that field is another alias,
[10], and to recent languages that are not delegation- the redirection continues recursively. (However,
based [9, 30]. aliasing operations are not themselves redirected; see
The embedding-based model is straightforward, and section 2.3.)
is well suited to network applications because of the As we said, Obliq fields (including methods) are
self-contained nature of the objects. The delegation- stored directly in objects, not indirectly in classes or
based model, in contrast, maximizes sharing across shared prototypes. Therefore, field lookup is a one-step
objects; this is not always desirable in a distributed process that searches a field by name within a single
context. For example, when an Obliq object is cloned object: there is no class or delegation hierarchy to be
over the network it carries with it its embedded searched iteratively. Field lookup is based on a nearly
constant-time caching technique that does not penalize
large objects. A separate cache is used for each
2 The terms concatenation-based and copy-based have also been
used.

May 30, 1995 9:22 PM Page 3


operation instance; the cache records the position ments are transmitted over the network to the remote
where a field was last found in an object [15]. site, the result is computed remotely, and the final
value (or error, or exception) is returned to the site of
2.2 Simple Examples the invocation.
Let us examine some simple examples, just to Updating (and Overriding)
became familiar with the Obliq syntax and semantics. A This operation deals with both value field update
full explanation of object operations is given in the next and method field override:
section.
The following object has a single method that a.x := b
invokes itself through self (the s parameter). A let Here the field x of a is updated with a new value b. If x
definition binds the object to the identifier o: contains a method and b is a method, we have method
let o = { x => meth(s) s.x() end }; override. If x and b denote ordinary values, we have
value update. The other two possibilities are also al-
An invocation of o . x ( ) results in a divergent lowed: a value field can be turned into a method field,
computation. Divergence is obtained here without any and vice versa.
explicit use of recursion: the self-application implicit in When a field of a remote object is updated, a value is
method invocation is sufficient. transmitted over the network and installed into the re-
The object below has three components: a value field mote object. Remote method override involves the
x, a method inc that increments x through self and re- transmission of a method closure.
turns self, and a method next that invokes i n c
Cloning
through self and returns the x component of the result.
Our third operation is object cloning, generalized to
let o = multiple objects:
{ x => 3,
inc => meth(s,y) s.x := s.x+y; s end, clone(a1, ... ,an)
next => meth(s) s.inc(1).x end }; In the case of a single argument, a new object is cre-
Here are some operations that can be performed on o: ated with the same field names as the argument object;
its fields are initialized to the similarly named values,
o.x Selecting the x component. methods, and aliases of the argument object.
o.x := 0 Setting the x component to zero. In the case of n≥2 arguments, a single object is pro-
o.inc(1) Invoking a method, with parameters. duced that contains the values, methods, and aliases of
o.next() Invoking a method with no all the argument objects (an error is given in case of
parameters. field name conflicts). Useful idioms are clone(a,
o.next := meth(s) clone(s).inc(1).x end {...}), to inherit the fields of a and add new fields,
Overriding the next method so that it and clone(a1, a2), to multiply inherit from a1 and a2.
no longer modifies its host object. When a collection of remote or local objects is
cloned, the clone is created at the local site. Its contents
2.3 Operations (including method closures) may have to be fetched
over the network.
We now examine the object operations in some
Aliasing
detail. Apart from object creation, there are four basic
operations on objects. Our final operation is aliasing, which is the replace-
ment of field contents with aliases (section 2.1). The
Selection (and Invocation)
syntax is similar to updating, but this is really a
This operation has two variants for value selection separate operation:
and method invocation:
a.x := alias y of b end
a.x
a.x(b1, ... ,bn) Further operations on x of a are redirected to y of b; ei-
ther object may be local or remote. An aliasing
The first form selects a value from a value field x of a operation replaces field contents with aliases regardless
and returns it. The second form invokes a method from of whether those fields are already aliased.
a method field x of a, supplies n≥0 parameters, and For a method invocation a.x(c), the field x =>
returns the result produced by the method; the object a alias y of b end behaves just like the field x =>
is bound to the self parameter of the method. For meth(s,z) b.y(z) end; that is, an aliased invocation
convenience, the first form can be used for invocation behaves like an indirect method invocation. However,
of methods with zero parameters. aliasing redirects also method override, as well as value
When a value field of a remote object is selected, its selection and value update.3
value is transmitted over the network to the site of the
selection (see the transmission semantics in section 1.2).
3 Note that, for simplicity, we delayed the discussion of
When a method of a remote object is invoked, the argu- redirection in our previous explanation of selection and update.

Page 4 May 30, 1995 9:22 PM


A special construct can be used to alias all the
components of an object at once: 2.5 Protected Objects
redirect a1 to a2 end It is useful to protect objects against certain external
operations, to safeguard their internal invariants.
The effect is to replace every field x i of a 1 (including
Protection is particularly important, for example, to
alias fields) with alias x i o f a 2 e n d ; this is
prevent clients from overriding methods of network
particularly useful for network redirection.
services, or from cloning servers. Even protected
Aliasing is implicit in the distributed-systems notion
objects, though, should be allowed to modify their own
of local surrogate of a remote object: we have simply
state and to clone themselves.
lifted this mechanism to the language level. By doing
A protected object is an object that rejects external up-
this, we are able to put network redirection under
date, cloning, and aliasing operations, but that admits
flexible program control, as shown later in the case of
such operations when they are self-inflicted. The syntax
object migration.
is:
For method invocation, aliasing redirections behave
differently from the redirections typical of delegation- { protected, x1 => a1, ... , xn => an }
based languages [22]: in aliasing, self is bound to the
Therefore, for example, methods of a protected object
redirection target; in delegation, self is bound to the
can update sibling fields through self, but external
redirection source. Aliasing is more satisfactory than
operations cannot modify such fields.
delegation when the redirection target is a remote
Note that a protection mechanism based on
object: after an initial aliasing redirection over the
individual fields would not address protection against
network, further accesses to self are local.
cloning.
2.4 Self-inflicted Operations 2.6 Serialized Objects
Our four basic object operations can be performed
An Obliq server object can be accessed concurrently
either as external operations on an object, or as internal
by multiple remote client threads. Moreover, local
operations through self. This distinction is useful in the
concurrent threads may be created explicitly. To
contexts of object protection and serialization,
prevent race conditions, it must be possible to serialize
discussed in the next two sections, which are essential
access to objects and their state.
features of distributed objects. In preparation, we
We say that an object is serialized when (1) at most
discuss the general notion of self-inflicted operations.
one thread at a time can operate on the object or run
When a method operates on an object other than the
one of its methods. Moreover, we want to ensure that
methodÕs host object, we say that the operation is
(2) a method can call a sibling through self without
external to the object. By contrast, when a method
deadlock. Note that requirement (2) does not contradict
operates directly on its own self, we say that the
invariant (1).
operation is self-inflicted:
The obvious approach to implementing serialized
¢ If op(o) has the form o.x, o.x:=b, clone(...,o,...), objects, adopted by many concurrent languages, is to
or o.x:=alias...end, then op(o) is self-inflicted (on associate a mutex with each object (for example, see [3]).
o) iff o is the same object as the self of the current Such mutexes are acquired when a method of an object
method. is invoked, and released when the method returns,
¢ op(o) is external (on o) iff it is not self-inflicted. guaranteeing condition (1). This way, however, we
have a deadlock whenever a method calls a sibling,
Here, by the current method (if it exists) we mean the last
violating condition (2). We find this behavior
method that was invoked in the current thread of
unacceptable because it causes innocent programs to
control and that has not yet returned. Therefore, the
deadlock without good reason. In particular, an object
notion of self for self-inflicted operations is preserved
that works well sequentially may suddenly deadlock
through procedure calls, but not through external
when a mutex is added. Brewer and Waldspurger [11]
method invocations or thread creation.
give an overview of previous solutions to this
Whether an operation is self-inflicted can be deter-
serialization problem.
mined by a simple run-time test. Consider, for example
A way to satisfy conditions (1) and (2) together is to
the object:
use reentrant mutexes, that is, mutexes that do not
{ p => meth(s) s.q.x end, q => ... } deadlock when re-locked by the ÒsameÓ thread (for
example, see [17]).
Here the operation s.q is self-inflicted, since s is self.
On the one hand, reentrant mutexes may be too
But the .x operation in s . q . x is self-inflicted
liberal, because they allow a method to call a method of
depending on whether s.q returns self; in general this
a different object, which then can call back a method of
can be determined only at run-time.
the present object without deadlocking. This goes well
beyond our simple desire that a method should be able
to call its siblings; object invariants may be

May 30, 1995 9:22 PM Page 5


compromised, since objects become vulnerable to The interaction of conditional synchronization with
unexpected activations of their methods. certain object operations requires some attention.
On the other hand, reentrant mutexes may be too re- Objects with implicit mutexes can be cloned: a fresh
strictive, because the notion of ÒsameÓ thread is implicit mutex is created for the clone. Consider then
normally restricted to an address space. If we want to the case of a thread blocked on a condition within an
consider control threads as extending across sites, then object that is being cloned: the thread remains blocked
an implementation of reentrant mutexes might not within the original object, not the clone. Consider now
behave appropriately. the case of a thread blocked on a condition within a
We solve the serialization problem by adopting an method that is being overridden or aliased. When the
intermediate locking strategy, which we call self thread resumes, the blocked method runs to completion
serialization, based on the notion of self-inflicted with a non-trivially modified self. Object protection,
operations described in section 2.4. when used in conjunction with serialization, alleviates
Serialized objects have an implicit associated mutex, these worries since it prevents external cloning and
called the object mutex. An object mutex serializes the updates.
execution of selection, update, cloning, and aliasing In summary, mutual exclusion, amended for self-in-
operations on its host object, according to the following flicted operations, handles common situations conve-
rules of acquisition: niently, for example for network servers maintaining
some internal state. In addition, conditional
¢ External operations always acquire the mutex of an
synchronization can be used for standard concurrency-
object, and release it on completion.
control problems. More complex situations may require
¢ Self-inflicted operations never acquire the mutex of
sophisticated uses of explicit mutexes; for this, Obliq
their object.
supports the full spectrum of Modula-3 thread
Note that a self-inflicted operation can happen only primitives [5, 20]. Explicit mutexes, conditions, and
after the activation of an external operation on the threads cannot be transmitted, since these values are
object that is executed by the same thread. The external strongly site-dependent.
operation has therefore already acquired the mutex. There is no automatic serialization for variables or
The serialization attribute of an object is specified as arrays. If necessary, their access can be controlled
follows: through serialized objects or explicit mutexes. Even for
objects, serialization is neither compulsory nor a
{ serialized, x1 => a1, ... ,xn => an }
default, since its use is not always desirable. In some
With self-serialization, a method can modify the state of cases it may be sufficient to serialize server objects (the
its host object and can invoke siblings without concurrent entry points to a site) and leave all other
deadlocking. A deadlock still occurs if, for example, a objects unserialized.
method invokes a method of a different object that then
attempts an operation on the original serialized object. 2.7 Name Servers and Execution Engines
A deadlock occurs also if a method forks an invocation
Obliq values can flow freely from site to site along
of a sibling and waits on the result.
communication channels. Such channels are initially es-
In addition to mutual exclusion, Obliq provides
tablished by interaction with a name server. A name
conditional synchronization over implicit object mutexes.
server for Obliq programs is an external process
Conditional synchronization (where threads wait on a
uniquely identified by an IP address; it simply
mutex and a condition) allows multiple threads to be
maintains a table associating text strings with network
simultaneously present ÒinsideÓ an object, although at
references [8].
most one thread is active at any time. Producer-
The connection protocol between two Obliq sites is
consumer behavior can be handled this way [5].
as follows. The first site registers a local, or remote,
A w a t c h statement is provided to wait on a
object under a certain name with a known name server.
condition in conjunction with the implicit mutex of an
The second site asks the name server for (the network
object. This statement must be used inside a method of
reference to) the object registered under that name. At
a serialized object; hence, it is always evaluated with
this point the second site acquires a direct network
the object mutex locked:
reference to the object living in the first site. The name
watch c until guard end server is no longer involved in any way, except that it
still holds the network reference. Obliq values and
The watch statement evaluates c to a condition and, if
network references can now flow along the direct
guard evaluates to true, terminates leaving the mutex
connection between the two sites, without having to be
locked. If the guard is false, the object mutex is un-
registered with a name server. This protocol is coded as
locked (so that other methods of the object can execute)
follows, using a built-in net module:
and the thread waits for the condition to be signaled.
When the condition is signaled, the object mutex is Server Site:
locked and the boolean guard is evaluated again, net_export("obj", Namer, site1Obj);
repeating the process.
Client Site:

Page 6 May 30, 1995 9:22 PM


let site1Obj = emphasis is on advanced, rather than tutorial,
net_import("obj", Namer); examples.
site1Obj.opA(args); (remote
invocation) 3.1 Recursion and Iteration
site3Obj.opB(site1Obj); (re-export to a third
We start with a simple example, to illustrate the use
site)
of definitions, local variables, and control constructs.
where "obj" is the registration name for the object, The factorial function is defined in recursive and
s i t e 1 O b j is the object, and N a m e r is a string iterative style.
containing the IP address or name of the machine
let rec recFact =
running the desired name server. The object is now
proc(n)
available through the name server, as long as the site
if n is 0 then 1
that exports it is alive. Objects are garbage collected at a
else n * recFact(n-1)
site when they are no longer referenced, either locally
end;
or via the network [6].
end;
We shall see soon that compute servers are definable
via simple network objects. However, compute servers let itFact =
are so common and useful that we provide them as proc(n)
primitives, calling them execution engines. An execution var cnt = n; var acc = 1;
engine accepts Obliq procedures (that is, procedure clo- loop
sures) from the network and executes them at the if cnt is 0 then exit end;
engine site. An engine can be exported from a site via acc := cnt * acc; cnt := cnt - 1;
the primitive: end;
acc;
Server Site:
end;
net_exportEngine("Engine1@Site1", Namer,
arg); Identifiers are declared by l e t, and updatable
variables by var. Recursive definitions are obtained by
The a r g parameter is supplied to all the client
l e t r e c . The identity predicate is called is. A
procedures received by the engine. It may contain local
sequence of statements separated by semicolons returns
data as well as site-specific procedures (services [29]).
the value of the last statement; hence the iterative
Multiple engines can be exported from the same site
factorial program returns acc.
under different names.
An engine, once imported, behaves like a procedure 3.2 The Object-Oriented Numerals
of one argument. Implementing engines as remote
procedures, instead of as remote objects, allows self- The next example illustrates the expressive power of
inflicted operations to extend across sites; this turns out the object primitives by encoding the natural numbers
to be important for object migration, as discussed in purely in terms of objects.
section 4.6. let zero =
A client may import an engine and then provide a { case =>
procedure to be executed remotely. proc(pz,ps) pz() end,
Client Site: succ =>
let atSite1 = meth(self)
net_importEngine("Engine1@Site1", let o = clone(self);
Namer); o.case := proc(pz,ps) ps(self) end;
atSite1(proc(arg) 3+2 end); o
end };
Communication failures produce exceptions that can
be trapped. These failures may mean that one of the The numeral zero has two fields. The succ field pro-
machines involved has crashed, or that an Obliq duces successive numerals by appropriately modifying
address space was terminated. There is no automatic the current numeral. The case field is used to discrimi-
recovery from network failures. nate on zero: the idiom (n.case)(proc() b end,
proc(p) c end) is read, informally, as Òif n is zero
3. Local Techniques then return b, else bind the predecessor of n to p and
return cÓ.
In this section we discuss a collection of single- The code of the succ method depends heavily on
threaded examples to illustrate ObliqÕs sequential fea- Obliq peculiarities: it clones self, and embeds the
tures. A collection of concurrent and distributed exam- current self into a procedure closure, so that it can be
ples is given in section 4; the impatient reader may used later. For example, the numeral one, computed as,
want to skip forward. In both these sections the zero.succ(), is:

May 30, 1995 9:22 PM Page 7


{ case => proc(pz,ps) ps(zero) end, let calc =
succ => (as for zero) } { arg => 0.0, (the ÒvisibleÓ argument display)
acc => 0.0, (the ÒhiddenÓ accumulator)
Hence, one.case(pz,ps) correctly applies ps to the
predecessor of one. enter => (entering a new argument)
To show that the encoding is fully general, we define meth(s, n)
the successor, the predecessor, and the test for zero pro- s.arg := n;
cedures: s
end,
let succ =
proc(n) n.succ end; add => (the addition button)
meth(s)
let pred =
s.acc := s.equals;
proc(n)
s.equals := meth(s) s.acc+s.arg end;
(n.case)(proc() zero end, proc(p) p
s
end)
end,
end;
sub => (the subtraction button)
let iszero =
meth(s)
proc(n)
s.acc := s.equals;
(n.case)
s.equals := meth(s) s.acc-s.arg end;
(proc() true end, proc(p) false end)
s
end;
end,

3.3 The Prime Numbers Sieve equals => (the result button, and operator stack)
meth(s) s.arg end,
This example shows an interesting case of methods
reset => (the reset button)
overriding themselves, and of objects replicating them-
meth(s)
selves by cloning. The program below prints the prime
s.arg := 0.0;
numbers when the method m of the sieve object is in-
s.acc := 0.0;
voked with successive integers starting from 2. Each
s.equals := meth(s) s.arg end;
time a new prime p is found, the sieve object clones
s
itself into two objects. One of the clones then
end };
transforms itself into a filter for multiples of p; non-
multiples are passed to the other clone. ok is a trivial For example:
constant.
calc.reset.enter(3.5).equals; (3.5)
let sieve = calc.reset.enter(3.5).sub.enter(2.0)
{ m => .equals; (1.5)
meth(s, n) calc.reset.enter(3.5).add.equals; (7.0)
print(n); (defined calc.reset.enter(3.5).add.add.equals; (10.5)
elsewhere)
let s0 = clone(s);
3.5 Surrogates
s.m :=
meth(s1,n1) Here we create a non-trivial surrogate for the
if (n1 % n) is 0 then ok calculator object of section 3.4. Unlike the original
else s0.m(n1) calculator, this object is protected against outside
end interference. Some of the calculator fields are shared by
end; aliasing, some are hidden, some are renamed, and one
end }; is added.
(print the primes < 100) let publicCalc =
for i = 2 to 100 do sieve.m(i) end; { protected,
enter => alias enter of calc end,
At any point in time, if n primes have been printed,
pi => meth(s) s.enter(3.141592) end,
then there exists n filter objects plus a clone of the
plus => alias add of calc end,
original sieve object.
minus => alias sub of calc end,
3.4 A Calculator equals => alias equals of calc end,
reset => alias reset of calc end };
This example illustrates method overriding, used
here to store the Òpending operationsÓ of a pocket
calculator.

Page 8 May 30, 1995 9:22 PM


read =>
4. Distributed Techniques meth(s)
watch nonEmpty (wait for writers)
In this section we code some distributed
until #(q)>0 (check size of queue)
programming techniques in Obliq. Each example is
end;
typical of a separate class of distributed programs, and
let q0 = q[0]; (get first element)
illustrates the unique features of Obliq.
q := q[1 for #(q)-1]; (remove from queue)
q0; (return first element)
4.1 A Serialized Queue
end; });
We begin with an example of ordinary concurrent
Let us see how this queue can be used. Suppose a
programming to illustrate the threads primitives that
reader is activated first when the queue is still empty.
are used in the sequel. We implement a queue that can
To avoid an immediate deadlock, we fork a thread
be accessed consistently by concurrent reader and
running a procedure that reads from the queue; this
writer threads.
thread blocks on the watch statement. The reader
The queue is implemented as a serialized object with
thread is returned by the fork primitive, and bound to
read and write methods. These methods refer to free
the identifier t:
identifiers that are hidden from users of the queue. The
object mutex is used, implicitly, to protect a private let t = (fork a reader t, which blocks)
variable that contains an array of queue elements. fork(proc() queue.read() end, 0);
Another private variable contains a condition nonEmpty
Next we add an element to the queue, using the current
used for signaling the state of the queue.
thread as the writer thread. A non-empty condition is
The write method adds an element to the queue, and
immediately signaled and, shortly thereafter, the reader
signals the non-empty condition, so that at least one
thread returns the queue element.
reader thread waiting on that condition wakes up (a
similar broadcast operation wakes up all waiting queue.write(3); (cause t to read 3)
threads). The object mutex is locked throughout the
The reader thread has now finished running, but is not
execution of the write method, therefore excluding
completely dead because it has not delivered its result.
other writer or reader threads.
To obtain the result, the current thread is joined with
When a read method starts executing, the object
the reader thread:
mutex is locked. Its first instruction is to watch for the
non-empty condition, and for the existence of elements let result = join(t); (get 3 from t)
in the queue. If the queue is non-empty, the reader
In general, join waits until the completion of a thread
simply goes ahead and removes one element from the
and returns its result.
queue. If the queue is empty, the reader thread is
suspended and the object mutex is released (allowing 4.2 Compute Servers
other reader and writer threads to execute). The reader
is suspended until it receives a signal for the non-empty The compute server defined below receives a client
condition; then the object mutex is locked, and the procedure p with zero arguments via the r e x e c
reader thread proceeds as above (possibly being method, and executes the procedure at the server site.
suspended again if some other reader thread has This particular server cheats on clients by storing the
already emptied the queue). latest client procedure into a global variable replay.
What is important here is that a reader thread may Another field, lexec, is defined similarly to rexec,
be blocked inside a method, and yet a writer thread can but rexec is a method field, while lexec is a value
get access and eventually allow the first thread to field containing a procedure: the operational difference
proceed. Hence, even though only one thread at a time is discussed below.
can run, multiple threads may be simultaneously Server Site:
present ÒinÓ the object. var replay = proc() end;
Here, [...] is an array, # is array-size, and @ is array- net_export("ComputeServer", Namer,
concatenation. { rexec => meth(s, p) replay:=p; p()
let queue = end,
(let nonEmpty = condition(); lexec => proc(p) replay:=p; p() end
var q = []; (the hidden queue data) });

{ protected, serialized, A client may import the compute server and send it
write => a procedure to execute. The procedure may have free
meth(s, elem) variables at the client site; in this example it increments
q := q @ [elem]; (append elem to tail) a global variable x:
signal(nonEmpty); (wake up readers)
end,

May 30, 1995 9:22 PM Page 9


report => meth ... end,
Client Site:
stop => meth ... end }
let computeServer =
end);
net_import("ComputeServer", Namer);
var x = 0; The execution of the client procedure causes the alloca-
computeServer.rexec(proc() x:=x+1 end); tion of an object at the server site with methods start,
(now x = 1) report, and stop, and with a state field. The server
simply returns a network reference to this object, and is
When the server executes its rexec method,
no longer engaged. (Client resources at the server site
replay is set to (a closure for) proc() x:=x+1 end at
are released when the client garbage collects the search
the server site, and then x is set to 1 at the client site,
agent, or when the client site dies [6].)
since the free x is lexically bound to the client site. Any
We show below an example of what the client can
variable called x at the server site, if it exists, is a
now do. The client starts a remote search via start
different variable and is not affected. At the server site
from background thread, and periodically request a
we may now invoke replay(), setting x to 2 at the
progress report via report. If the search is successful
client site.
within a given time period, everything is fine. If the
For contrast, consider the execution of the following
search takes too long, the remote agent is aborted via
line at the client site:
stop. If an intermediate report proves promising, the
Client Site: client may decide to wait for however long it takes for
(computeServer.lexec)(proc() x:=x+1 end); the agent to complete, by joining the background
thread.
This results in the server returning the procedure
proc(p) replay:=p; p() end to the client, by the se- DataBase Client Site:
mantics of remote field selection, with replay bound let searchThread =
at the server site. Then the client procedure proc() fork(proc() searchAgent.start() end, 0);
x:=x+1 end is given as an argument. Hence, this time,
var report = "";
the client procedure is executed at the client site. Still,
for i = 1 to 10 do
the execution at the client site causes the client
pause(6.0);
procedure to be transmitted to the server and bound to
report := searchAgent.report();
the replay variable there. The final effect is the same.
if successful(report) then exit end;
4.3 Remote Agents if promising(report) then
report := join(searchThread); exit;
Execution engines (section 2.7) can be used as end;
general object servers; that is, as ways of allocating end;
objects at remote sites. These objects can then act as searchAgent.stop();
agents of the initiating site, supporting multiple
This technique for remotely allocating objects can be
requests.
extended to multiple agents searching multiple
Suppose, for example, that we have an engine ex-
databases simultaneously, and to agents initiating their
ported by a database server site. The engine provides
own sub-agents.
the database as an argument to client procedures:
DataBase Server Site: 4.4 Application Partitioning
net_exportEngine("DBServer", Namer,
The technique for remotely allocating objects
dataBase);
described in section 4.3 can be used for application
A database client could simply send over procedures partitioning. An application can be organized as a
performing queries on the database (which, for collection of procedures that return objects. When the
complex queries, would be more efficient than application starts, it can pick a site for each object and
repeatedly querying the server remotely [19, 29]). send the respective procedure to a remote engine for
However, for added flexibility, the client can instead that site. This way, the application components can be
create an object at the server site that acts as its remote (initially) distributed according to dynamic criteria.
agent:
4.5 Agent Migration
DataBase Client Site:
let atDBServer = In this example we consider the case of an
net_importEngine("DBServer", Namer); untethered agent that moves from site to site carrying
along some state [34]. We write the state as an object,
let searchAgent =
and the agent as a procedure parameterized on the
atDBServer(
state and on a site-specific argument:
proc(dataBase)
{ state => ...,
start => meth ... end,

Page 10 May 30, 1995 9:22 PM


state. This can be achieved by serializing the migrating
let state = { ... };
object, and by invoking the migrateProc procedure
let agent = proc(state, arg) ... end;
from a method of that object, where it is applied to self:
To be completely self-contained, this agent should have
let obj1 =
no free identifiers, and should use the state parameter
{ serialized, protected,
for all its long-term memory needs.
... (other fields)
The agent can be sent to a new site as follows,
migrate =>
assuming atSite1 is an available remote engine:
meth(self, engineName)
atSite1( migrateProc(self, engineName);
proc(arg) agent(copy(state),arg) end) end };
The copy operation is explained below, but the intent let remoteObj1 =
should be clear: the agent is executed at the new site, obj1.migrate("Engine1@Site1")
with a local copy of the state it had at the previous site.
Because of serialization, the object state cannot change
The agentÕs state is then accessed locally at the new site.
during a call to migrate. The call returns a network
Implicitly, we assume that the agent ceases any activity
reference to the remote clone, which can be used in
at the old site. The agent can repeat this procedure to
place of obj1 (which, anyway, has been aliased to the
move to yet another site.
clone).
The copy operation is a primitive that produces
We still need to explain how migration can work for
local copies of (almost) arbitrary Obliq values,
protected objects, since such objects are protected
including values that span several sites. Sharing and
against external cloning and aliasing. Note the
circularities are preserved, even those that span the
migrateProc (self, ...) call above, where self is
network. Not all values can be copied, however,
bound to o b j 1 . It causes the execution of
because not all values can be transmitted. Protected
engine(proc(arg) clone(obj1) end). Rather
objects cause exceptions on copying, as do site-specific
subtly, the cloning of obj1 here is self-inflicted (section
values such as threads.
2.4), even though it happens at a site different from the
This techniques allows autonomous agents to travel
site of the object. According to the general definition,
between sites, perhaps eventually returning to their
clone(obj1) is self-inflicted because obj1 is the
original site with results. The original site may go off-
same as the self of the last active method of the current
line without directly affecting the agent.
thread, which is migrate (an engine call behaves like a
The main unpleasantness is that, because of copying,
procedure call). The redirection operation is similarly
the state consistency between the old site and the new
self-inflicted. Therefore, the protected status of obj1
site must be preserved by programming convention (by
does not inhibit self-initiated migration.
not using the old state). In the next section we see how
Migration permanently modifies the original object,
to migrate state consistently, for individual objects.
redirecting operations to the remote clone. In
4.6 Object Migration particular, if obj1 is asked to migrate again, the remote
clone will properly migrate.
In this example we use a remote execution engine to We can avoid chains of indirections if the migrating
migrate an object between two sites. First we define a object obj1 is publicly available through a name
procedure that, given an object, the name of an engine, server. The migrate method can then register the
and a name server, migrates the object to the engineÕs migrated object with the name server under the old
site. Migration is achieved in two phases: (1) by causing name:
the engine to remotely clone the object, and (2) by
let obj1 =
aliasing the original object to its remote clone (section
net_export("obj1", Namer,
2.3).
{ serialized, protected,
let migrateProc = ...
proc(obj, engineName) migrate =>
let engine = meth(self, engineName)
net_importEngine(engineName, Namer); net_export("obj1", Namer,
let remoteObj = migrateProc(self, engineName));
engine(proc(arg) clone(obj) end); (1) end };
redirect obj to remoteObj end; (2)
This way, old clients of o b j 1 go through aliasing
remoteObj;
indirections, but new clients acquiring obj1 from the
end;
name server operate directly on the migrated object.
After migration, operations on the original object are
redirected to the remote site, and executed there.
It is critical that the two phases of migration be exe-
cuted atomically, to preserve the integrity of the object

May 30, 1995 9:22 PM Page 11


distributed computation, and by the availability of a
4.7 Application Servers sophisticated network-objects implementation
technology. The Obliq object primitives were designed
Visual Obliq [4] is an interactive distributed-applica-
in parallel with work on the semantics and type theory
tion and user-interface generator, based on Obliq. All
of objects [1]; distributed scoping and distributed
distributed applications built in Visual Obliq follow the
semantics, however, are not treated there.
same model, which we may call the application server
model. In this model, a centralized server supplies Influence of Modula-3 Network Objects
interested clients, dynamically, with both the client The characteristics of Modula-3 Network Objects
code (as a closure) and the client user interface of a (M3NOs) had a major influence on the Obliq language
distributed application. The closure transmitted to each design and implementation. Thanks to the low
client retains lexical bindings to the server site, overhead involved, all Obliq objects are M3NOs, so
allowing it to communicate with the server and with there is no artificial separation between local objects,
other clients. Each client may have independent local and objects that may be remotely accessed. Similarly,
state, and may present an independent view of the all Obliq program variables (declared by v a r) are
application to the user. A typical example is a M3NOs: this is the basis for distributed lexical scoping.
distributed tic-tac-toe game. Concerns about space reclamation, specially for
resources used by remote agents, are relieved by
5. Conclusions distributed garbage collection of M3NOs. Finally, the
M3NOs stub generator handles automatically the
Obliq addresses a very dynamic form of distributed
transmission of all of ObliqÕs run-time structures.
programming, where objects can redirect their behavior
Moral: a distributed language like Obliq is easy to
over the network, and where computations can roam
implement on top of a library like Modula-3 Network
between network sites. We feel that this kind of
Objects. Conversely, a network object library should
programming is still in its infancy, and that not all the
make it easy to implement a language like Obliq, or is
fundamental issues can yet be addressed at once.
falling short of some important goals.
Where in doubt, we have given precedence to flexible
mechanism over robust methodology, hoping that Status
methodology will develop with experience. In this Obliq has been available at Digital SRC for about a
spirit, for example, Obliq could be used to experiment year and a half. In addition to incidental programming,
in the design and implementation of agent/place it has been used extensively as a scripting language for
paradigms [34], using the basic techniques of section 4. algorithm animation [13] and 3D graphics [25], and as
Related Work the basis of the Visual Obliq distributed-application
builder [4]. The Obliq implementation provides access
ObliqÕs features and application domains overlap
to many popular Modula-3 libraries [20], and to an
with programming languages such as ML [24, 28],
extensive user interface toolkit [14] including digital
Modula-3 [26], and Self [33]; with scripting languages
video [18]. Obliq can be used as a stand-alone
such as Sundew [19], Tcl [27], AppleScript [2], VBA [12,
interactive interpeter. It can also be embedded as a
23], and Telescript [34]; and with distributed languages
library in Modula-3 applications, allowing them to
such as Emerald [21], Orca [3], FortŽ [17], and Facile
interact remotely through Obliq scripts [16]. The
[32]. None of these languages, however, has the same
implementation and documentation are available on
mix of features as Obliq, particularly concerning the
the World Wide Web at http:-/ /www.-research.-digital.-
distribution aspects.
com/-SRC/-home.-html.
Stamos and Gifford [29] eloquently describe remote
execution as a generalization of remote procedure call, Future Work
and survey previous work on remote execution mecha- Issues of authentication, security, authority
nisms. Their proposal, though, restricts the delegation, and accounting are being explored.
transmission of higher-order procedures and Acknowledgments
procedures with free identifiers, inhibiting the
techniques of section 4. The Network Objects project at Digital SRC
Our choice of features was largely determined by the provided the infrastructure without which Obliq would
idea of a distributed lexically scoped language, by the not have been conceived. Alan Knaff implemented the
desire for a simple object model that would scale up to metaparser layer of the Obliq parser.

Appendix: Syntax Overview (See reference [16] for details.)

TOP-LEVEL PHRASES
a; any term or definition ended by ;
DEFINITIONS (denoted by d; identifiers are denoted by x, terms are denoted by a)

Page 12 May 30, 1995 9:22 PM


let x1=a1,...,xn=an definition of constant identifiers
let rec x1=a1,...,xn=an definition of recursive procedures
var x1=a1,...,xn=an definition of updatable identifiers
SEQUENCES (denoted by s)
a 1;...;an executes each a i (term or defin.); yields a n (or ok if
n=0)
TERMS (denoted by a, b, c; identifiers are denoted by x, l; libraries are denoted by m)
x | m_x identifiers
x:=a assignment
ok | true | false | 'a' | "abc" | 3 | 1.5 constants
[a 1,...,an] arrays
a[b] | a[b]:=c array selection, array update
a[b1 for b2] | a[b1 for b2]:=c subarray selection, subarray update
option l => s end term s tagged by l
proc(x1,...,xn) s end procedures
a(b1,...,bn) procedure invocation
m_x(a1,...,an) invocation of x from library m
a b c infix (right-associative) version of b(a,c)
meth(x,x1,...,xn) s end method with self x
{l1=>a1,...,ln=>an} object with fields named l1...ln
{protected, serialized, ...} protected and serialized object
{l1=>alias l2 of a2 end,...} object with aliased fields
a.l | a.l(a1, ..., an) field selection / method invocation
a.l:=b field update / method override
clone(a1,...,an) object cloning
a1.l1:=alias l2 of a2 end field aliasing
redirect a1 to a2 end object aliasing
d definition
if s1 then s2 elsif s3 then s4... else sn end conditional (elsif, else optional)
a andif b | a orif b conditional conjunction/disjunction
a is b | a isnot b identical/not identical predicates
case s of case over the tag li of an option value
l1(x1)=>s 1,...,ln(xn)=>s n else s0 end binding xi in si (else optional)
loop s end loop
for i=a to b do s end iteration through successive integers
foreach i in a do s end iteration through an array
foreach i in a map s end yielding an array of the results
exit exit the innermost loop, for, foreach
exception("exc") new exception value named exc
raise(a) raise an exception
try s except a1=>s1,...,an=>sn else s0 end exception capture (else optional)
try s1 finally s2 end finalization
condition() | signal(a) | broadcast(a) creating and signaling a condition
watch s1 until s2 end waiting for a signal and a boolean guard
fork(a1,a2) | join(a) forking and joining a thread
pause(a) pausing the current thread
mutex() creating a mutex
lock s1 do s2 end locking a mutex in a scope
wait(a1,a2) waiting on a mutex for a condition
(s) block structure / precedence group

May 30, 1995 9:22 PM Page 13


References [17] FortŽ, TOOL reference manual. FortŽ, Inc. 1994.
[18] Freeman, S.M.G. and M.S. Manasse, Adding
[1] Abadi, M. and L. Cardelli, A theory of primitive digital video to an object-oriented user interface
objects: untyped and first-order systems. Proc. toolkit. Proc. ECOOPÕ94. Springer-Verlag. 1994.
Theoretical Aspects of Computer Software. Springer-
Verlag. 1994. [19] Gosling, J., Sundew: a distributed and extensible
window system. Proc. Winter Usenix Technical Con-
[2] Apple, AppleScript Language Guide. Addison ference. Usenix Association. 1986.
Wesley. 1993.
[20] Horning, J., B. Kalsow, P. McJones, and G. Nelson,
[3] Bal, H.E., M.F. Kaashoek, and A.S. Tanenbaum, Some useful Modula-3 interfaces. Report 113.
Orca: a language for parallel programming of Digital Equipment Corporation, Systems Research
distributed systems. IEEE Transactions on Software Center. 1993.
Engineering 18(3), 190-205. 1992.
[21] Jul, E., H. Levy, N. Hutchinson, and A. Black,
[4] Bharat, K. and M.H. Brown, Building distributed Fine-grained mobility in the Emerald system.
applications by direct manipulation. P r o c . ACM Transactions on Computer Systems 6(1), 109-
UISTÕ94. 1994. 133. 1988.
[5] Birrell, A.D., An introduction to programming [22] Lieberman, H., Using prototypical objects to im-
with threads. In Systems Programming with Modula- plement shared behavior in object oriented sys-
3, Chapter 4, G. Nelson, ed. Prentice Hall. 1991. tems. Proc. OOPSLAÕ86. ACM Press. 1986.
[6] Birrell, A.D., D. Evers, G. Nelson, S. Owicki, and E. [23] Mansfield, R., Visual Basic for Applications. Ven-
Wobber, Distributed garbage collection for net- tana Press. 1994.
work objects. Report 116. Digital Equipment
Corporation, Systems Research Center. 1993. [24] Milner, R., M. Tofte, and R. Harper, The
definition of Standard ML. MIT Press. 1989.
[7] Birrell, A.D., G. Nelson, S. Owicki, and E. Wobber,
Network objects. Proc. 14th Symposium on [25] Najork, M. and M.H. Brown, A library for
Operating Systems Principles. 1993. visualizing combinatorial structures. Proc. IEEE
VisualizationÕ94. 1994.
[8] Birrell, A.D., G. Nelson, S. Owicki, and E. Wobber,
Network objects. Report 115. Digital Equipment [26] Nelson, G., ed. Systems programming with Mod-
Corporation, Systems Research Center. 1994. ula-3. Prentice Hall. 1991.
[9] Blaschek, G., Type-safe OOP with prototypes: the [27] Ousterhout, J.K., Tcl and the Tk toolkit. Addison-
concepts of Omega. Structured Programming Wesley. 1994.
12(12), 1-9. 1991. [28] Reppy, A higher-order concurrent language. Proc.
[10] Borning, A.H., Classes versus prototypes in SIGPLANÕ91 Conference on Programming Language
object-oriented languages. Proc. ACM/IEEE Fall Design and Implementation. ACM Press. 1991.
Joint Computer Conference. 1986. [29] Stamos, J.W. and D.K. Gifford, R e m o t e
[11] Brewer, E.A. and C.A. Waldspurger, Preventing Evaluation. ACM Transactions on Programming
recursion deadlock in concurrent object-oriented Languages and Systems 12(4), 537-565. 1990.
systems. Proc. 1992 International Parallel Processing [30] Taivalsaari, A., Kevo, a prototype-based object-
Symposium, Beverly Hills, California. (Also, Report oriented language based on concatenation and
MIT/LCS/TR-526.). 1992. module operations. Report LACIR 92-02.
[12] Brockschmidt, K., Inside OLE2. Microsoft Press. University of Victoria. 1992.
1994. [31] Taivalsaari, A., A critical view of inheritance and
[13] Brown, M.H., Report on the 1993 SRC algorithm reusability in object-oriented programming.
animation festival. Report n.126. Digital JyvŠskylŠ Studies in computer science, economics
Equipment Corporation, Systems Research Center. and statistics No.23, A. Salminen ed. University of
To appear. 1994. JyvŠskylŠ. 1993.
[14] Brown, M.H. and J.R. Meehan, The FormsVBT [32] Thomsen, B., L. Leth, S. Prasad, T.-M. Kuo, A.
Reference Manual. Unpublished. Digital Kramer, F. Knabe, and A. Giacalone, Facile
Equipment Corporation, Systems Research Center. Antigua Release Programming Guide. ECRC-93-
1994. 20. European Computer-Industry Research Centre.
1993.
[15] Cardelli, L., The Amber machine. P r o c .
Combinators and Functional Programming Languages. [33] Ungar, D. and R.B. Smith, Self: the power of sim-
Lecture Notes in Computer Science 242. Springer- plicity. Lisp and Symbolic Computation 4(3). 1991.
Verlag. 1986. [34] White, J.E., Telescript technology: the foundation
[16] Cardelli, L., Obliq: A language with distributed for the electronic marketplace. White Paper. Gen-
scope. Report n.122. Digital Equipment eral Magic, Inc. 1994.
Corporation, Systems Research Center. 1994.

Page 14 May 30, 1995 9:22 PM

You might also like