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 ;
Point: adt {
x, y: int;
add: fn (p: Point, q: Point): Point;
eq: fn (p: Point, q: Point): int;
};
r, s: Point; xcoord: int; ... xcoord = s.x; r = r.add(r, s);
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 §8.13 below. 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(r: self Rect, p: Point): int;
};
r1: Rect;
p1: Point;
...
if (r1.contains(p1)) ...
If self is specified in the declaration of a function, it must also be specified in the definition as well. For example, contains would be defined
Rect.contains(r: self Rect, p: Point)
{
. . .
}
The adt type in Limbo does not provide control over the visibility of its individual members; if any are accessible, all are.
The cyclic modifier will be discussed in §11.1.