Open and closed objects
Fabl uses the OWL formalism for defining classes. This results in a class system that departs significantly from those found in traditional strongly-typed programming environments such as Java,C++, or C#. In these traditional environments, a class is the mechanism by which legal properties (aka members or instance variables) of objects (aka resources) are introduced. That is, an object as traditionally defined may possess only those properties explicitly mentioned in the definition of the classes to which it belongs. Furthermore, once the object is created, no new properties can be added. In this sense, objects in traditional environments can be characterized as closed.
In OWL, the opposite approach is taken: classes serve to restrict an otherwise free graph of resources and properties. An object may possess any property with any value, except as restricted by class definitions (or by domain and range restrictions on properties). Furthermore, an object may be assigned new properties, and take on new class memberships, over the course of time. In this sense, OWL (and therefore Fabl) supports open objects.
Concretely, in OWL (and Fabl) a class denotes a set of RDF resources. A class is defined by specifying conditions that must be met by resources belonging to the class, or by specifying relationships to other classes that have already been defined.
Direct conditions on member resources are expressed by property restrictions. A property restriction asserts that a property, when applied to a resource in the class, must take on a set of values with restricted type, value or cardinality.Class definition syntax
In Fabl, classes can be defined by direct use of OWL primitives. However, an alternative syntax is provided that is more concise and legible. This syntax will be described first, followed by the details of how it is implemented using OWL.
The function
void class(string name)
introduces a new class with the given qualified name, and switches the Fabl interpreter into a special mode in which restrictions can be asserted. The syntax for restriction statements is:
restrict Property {restriction0;...;restrictionn}
The following varieties of restriction are recognized by Fabl. Each restriction
applies to the property when applied to members of the class being defined
(and not to the property applied to other varieties of object). Links are included
to the corresponding OWL definitions.
maxCardinality 1;
asserts that the property has at most one value. owl:maxCardinality
cardinality 1;
asserts that the property has exactly one value. owl:cardinality
allValuesFrom Type;
asserts that all values of the property must belong to the the given Type. owl:allValuesFrom
hasValue value;
asserts that the property must take on the specified value; the property may take on other values as well if its cardinality in not restricted. owl:hasValue
OWL supports additional kinds of restrictions, and also additional kinds of assertions about classes, beyond those covered by Fabl restriction statements. These additional constructs can be formulated as direct RDF assertions in the Fabl context, but the Fabl implementation makes no computational use of them (hence their exclusion, at least for now, from the restrict syntax).
In version 2.0.11, only those OWL constructs used in Fabl are present in the initial Fabl environment (eg owl:intersectionOf is not present in the Fabl triple store at start up).
The mode in which restrictions are added to a class is terminated by the function:void endClass()
The variant:
void class(string name,SeqOf(Class) subclasses)
asserts that the new class is an rdfs:subClassOf of each of the classes supplied as the second argument.
Example
In the following example, the class Point having x and y coordinates is defined in the geom2d namespace.
namespace('geom2d',"http://nurl.org/0/geom2d/1.0/");
allocate("geom2d:x",FunctionalProperty);
allocate("geom2d:y",FunctionalProperty);
class("geom2d:Point");
restrict geom2d:x {cardinality 1;allValuesFrom double}
restrict geom2d:y {cardinality 1;allValuesFrom double}
endClass();
The effect of the line
restrict geom2d:x {cardinality 1;allValuesFrom double}
is to assert that the property geom2d:x, when applied to any object in the class geom2d:Point, takes on exactly one value, and that the value is of type double.
Given the above class definition, Fabl is able to determine the type and cardinality of E.geom2d:x for expressions E of type geom2d:Point. In consequence, code such as the following can be implemented to manipulate members of the new class:
geom2d:Point function plus(geom2d:Point p,geom2d:Point q)
{
var geom2d:Point rs;
rs = new(geom2d:Point);
rs . geom2d:x = p.geom2d:x + q.geom2d:x;
rs . geom2d:y = p.geom2d:y + q.geom2d:y;
return rs;
}
Here is alternative Fabl code that implements geom2d:Point using OWL primitives directly:
allocate("geom2d:Point",Class);
// geom2d:x is a double
var r = new(owl:Restriction);
r.owl:onProperty = geom2d:x;
r.owl:allValuesFrom = double;
geom2d:Point..rdfs:subClassOf = r;
//geom2d:x takes on exactly one value:
var r = new(owl:Restriction);
r.owl:onProperty = geom2d:x;
r.owl:cardinality = 1;
geom2d:Point..rdfs:subClassOf = r;
// geom2d:y is a double
var r = new(owl:Restriction);
r.owl:onProperty = geom2d:y;
r.owl:allValuesFrom = double;
geom2d:Point..rdfs:subClassOf = r;
//geom2d:y takes on exactly one value:
var r = new(owl:Restriction);
r.owl:onProperty = geom2d:y;
r.owl:cardinality = 1;
geom2d:Point..rdfs:subClassOf = r;
initialize(geom2d:Point);
The initialize function:
void initialize(Class cl)
sets up internal data strutures whose purpose is to make Fabl's determination of type information more efficient. endClass accomplishes the same purpose in the context of the syntax described earlier. initialize should be performed after all subclass properties of a new class have been asserted.In Fabl, new classes can be added as a computation progresses simply by asserting new values of the property rdf:type. Here is an example:
var p = new(Resource); // p is not a Point yet p .. rdf:type = geom2d:Point; // now it is; // however the global variable p is still // of type Resource, not geom2d:Point var q = p ~ geom2d:Point; q.geom2d:x; -->0.0
Methods
The role served by methods in traditional environments is taken over in Fabl by other, simpler, constructs. Concretely, the role of a static method M of a class C is served by a polymorphic function that takes values of type C as its first argument. Instead of writing
x.M(a0,..an)
one writes
M(x,a0,...an)
A dynamic method is represented simply as a property with functional values, as in
restrict mymethod {allValuesFrom Function(resulttype, inputType0, .. inputTypen)}
The elaborate support
for methods in traditional environments serve many purposes related
to both to computational efficiency and expressiveness, and no claim is made
here that Fabl addresses all of these purposes with simlilar effectiveness.
The point is only that
the basic purposes served by methods - associating
behaviors with classes - are addressed by other mechanisms in Fabl.