Fabl allows the implementation of a function on one class
to be delegated to functions defined on other classes. A delegated
function in Fabl resembles an abstract method in Java or C# (or a virtual function in C++),
in that its implementation is missing from the class for which it is declared, and comes from elsewhere
- from derived classes in the case of Java,C#, or C++. Similarly, a Fabl class all of whose functions are delegated
plays the same role as an abstract class in traditional object oriented environments.
The primitive:
void delegate(<FunctionType> afn,<FunctionType> imp)
delegates the implementation of afn to imp. The types of afn and imp must be identical, except that the type Atp of the first argument to afn differs from the type Itp of the first argument to imp. Then, when afn is called with a first argument value which has type Itp, imp is invoked. An example will clarify the idea. Consider the following code from the 2d geometry code library:
// This function will be delegated
var Function(geom2d:Box,geom2d:Shape) bounds;
geom2d:Box function bounds(geom2d:Circle c)
{
var double r,cx,cy,geom2d:Point cnt;
r = c.geom2d:radius;
cnt = c.geom2d:center;
cx = cnt.geom:x;
cy = cnt.geom:y;
return mkBox(cx-r,cx+r,cy-r,cy+r);
}
delegate(bounds[geom2d:Shape],bounds[geom2d:Circle]);
Then, in
var Shape shp; var geom2d:Box bx; shp = geom2d:mkCircle(geom2d:mkPoint(1,2),1); bx = bounds(shp);
the call bounds(shp) determines at runtime that the value of shp has type geom2d:Circle.Then, since the function bounds[geom2d:Shape] delegates to bounds[geom2d:Circle] on circles, the latter function is invoked. There are several subclasses of shape for which a bounds function is implemented (eg geom2d:Polyline, geom2d:Segment, geom2d:Box, geom2d:Point), and for each of these, the "abstract" function bounds[geom2d:Shape] is delegated to the implementation in question. The parallel to abstract methods in traditional object-oriented languages such as Java should be apparent.
In the above example, bounds[geom2d:Shape] has no implemenation. However, it is permissable for a delegated function to have an implemention. This implementation serves as a kind of default : it will be called in the case where there is no delegate that applies to the runtime type(s) of the first argument.
Since a Fabl object x can have many types, and since a function f can have many delegates, it is possible that more than one delegate is a candidate implementation for the call f(x). In this case, the choice of delegate is the one corresponding to the last type for which a delegate is available in the ordered sequence of types stored with x. The order of this type list, in turn, places subclasses after superclasses, and more recently added types (added eg with x ..rdf:type = <newType>) after less recently added types. The net effect is similar to that which results from the rules of inheritance for traditional object oriented languages.