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

ADT Declarations

An adt, or abstract data type, contains data objects and functions that operate on them. The syntax is

adt-declaration:
	identifier : adt {adt-member-listopt}

adt-member-list:
	adt-member
	adt-member-list adt-member

adt-member:
	identifier-list : cyclicopt data-type;
	identifier-list : function-type;
	identifier-list : con expression;

After an adt-declaration, the identifier becomes the name of the type of that adt.

For example, after:

Point: adt {
	x, y :	 int;
	add :	fn (p : Point, q : Point) : Point;
	eq :	fn (p : Point, q : Point) : int;
};

the name Point is a type name of an adt of two integers and two functions; the fragment:

r, s : Point;
xcoord : int;
...
xcoord = s.x;
r = r.add(r, s);

makes sense. The first assignment selects one of the data members of s; the second calls one of the function members of r.

As this example indicates, adt members are accessed by mentioning an object with the adt type, a dot, and then the name of the member; the details will be discussed in Selection.

A special syntactic indulgence is available for functions declared within an adt: frequently such a function receives as an argument the same object used to access it (that is, the object before the dot). In the example just above, r was both the object being operated on and the first argument to the add function. If the first formal argument of a function declared in an adt is marked with the self keyword, then in any calls to the function, the adt object is implicitly passed to the function, and is not mentioned explicitly in the actual argument list at the call site. For example, in:

Rect: adt {
	min, max : Point;
	contains : fn (self Rect, Point) : int;
};

r1 : Rect;
p1 : Point;
...
if (r1.contains (p1)) ...

because the first argument of the contains function is declared with self, the subsequent call to it automatically passes r1 as its first argument. The contains function itself is defined elsewhere with this first argument explicit. (This mechanism is analogous to the this construct in C++ and other languages, but puts the special-casing at the declaration site and makes it explicit.)

If self is specified in the declaration of a function, it also must be specified in the definition as well. For example, contains would be defined:

Rect.contains (r : self Rect, p : Point) : int
{
	. . .
}

Since abstract data types have value semantics, the contains function receives a copy of the adt that is the implicit first argument and is unable to modify the original adt. Should an adt specific function have to modify an adt, it should be written with a self ref applied to the first argument type.

The adt type in Limbo does not provide control over the visibility of its individual members; all members are accessible.

The cyclic modifier is discussed in Forward Referencing <key> cyclic<kw> </key>.



[Contents]