[Contents]
[Prev] [Next] [Limbo Basics] [Limbo Programming] [Language Definition]

Forward Referencing

In general, names must be declared before they are used.

The first exception to this rule is that a function local to a module need not have a declaration at all; it is sufficient to give its definition, and that definition can appear anywhere in the module.

The general rule implies that no abstract data type can contain, as a member, an adt not previously declared (including an instance of itself). A second exception to this rule applies to ref adt types. An adt can contain a member whose type is a ref to itself, or to another adt even if the second adt has not yet been declared. Unless a special notation is used, such references are restricted: all mutual or self references among adt's are checked statically throughout all the adt's visible in a module to determine which adt members refer to other adt. Any member of an adt of ref adt type that refers directly, or indirectly through a chain of references, back to its own underlying type can not be assigned to individually; it can gain a value only by an assignment to the adt as a whole.

For example, in:

Tree : adt {
	l : ref Tree;
	r : ref Tree;
	t : ref Ntree;
};
Ntree : adt {
	t : ref Tree;
};

t1 := Tree(nil, nil, nil);					# OK
t2 := Tree(ref t1, ref t1, nil);					# OK
t1 = Tree(ref t1, ref t2, nil);					# OK
t1.l =... ;					# not OK

nt := ref Ntree(nil);					# OK
nt.t = ...					# not OK

the first three assignments are correct, but any assignment to t1.l is forbidden, because it is self-referential. The situation is the same with the mutually referential fields of the Tree and Ntree adt.

These restrictions suffice to prevent the creation of circular data structures. Limbo implementations guarantee to destroy all data objects not involved in such circularity immediately after they become non-referenced by active tasks, whether because their names go out of scope or because they are assigned new values. This property has visible effect because certain system resources, like windows and file descriptors, can be seen outside the program. In particular, if a reference to such a resource is held only within an adt, then that resource too is destroyed when the adt is.

The default rules are burdensome because they impede the construction even of harmless structures like trees. Therefore an escape is provided: using the word cyclic before the type in an adt member removes the circular-reference restriction for that member.

For example:

Tree : adt {
	l : cyclic ref Tree;
	r : cyclic ref Tree;
	t : ref Ntree;
};
Ntree : adt {
	t : cyclic ref Tree;
};

t1 := Tree(nil, nil, nil);						# OK
t2 := Tree(ref t1, ref t1, nil);						# OK
t1 = Tree(ref t1, ref t2, nil);
t1.l = ... ;						# OK now

nt := ref Ntree(nil); 						# OK
nt.t = ...						# OK now

With the use of cyclic, circular data structures can be created. When they become unreferenced except by themselves, they will be garbage-collected eventually, but not instantly.



[Contents]
[Prev] [Next] [Limbo Basics] [Limbo Programming] [Language Definition]

Copyright © 1998, Lucent Technologies, Inc. All rights reserved.