Introduction to DAML: Part II
March 13, 2002
Roxane Ouellet and Uche Ogbuji
Overview
RDF was developed by the W3C at about the same time as XML, and it turns out to be an excellent complement to XML, providing a language for modeling semistructured metadata and enabling knowledge-management applications. The RDF core model is successful because of its simplicity. The W3C also developed a purposefully lightweight schema language, RDF Schema (RDFS), to provide basic structures such as classes and properties.
As the ambitions of RDF and XML have expanded to include things like the Semantic Web, the limitations of this lightweight schema language have become evident. Accordingly, a group set out to develop a more expressive schema language, DARPA Agent Markup Language (DAML). Although DAML is not a W3C initiative, several familiar faces from the W3C, including Tim Berners-Lee, participated in its development.
The previous article in this series presented basic DAML concepts and constructs, explaining the most useful modeling tools DAML puts into the designer's hands.
The present article demonstrates more advanced DAML concepts and constructs, expanding on the Super Sports example.
So far we have looked at how DAML+OIL gives us richer means for expressing constraints in schemas. If this were all it did, it would still be a welcome advance over RDFS. But it happens to go well beyond that. DAML+OIL gives modelers a rich expressiveness. It is not just a schema language but also an ontology language, providing primitives that support the general representation of knowledge. For one thing, it allows one to express classifications by inference rather than by explicitly listing which resources go into which buckets. Behind this simply-stated idea lies a surprising range of nuance for accommodating the classic difficulty of translating the models we hold in our minds to the models we mold in our code.
A Closer Look at daml:Class
The class daml:Class
, as we've seen, is a subclass of rdfs:Class
that adds some useful features such as support for classes defined as enumerations.
Disjoint classes
Another useful feature is the ability to state that one class is disjoint from another. This means that neither of the two classes have any instances in common. So, for example, if we wanted to state that something is either a current product or a discontinued product (in order to keep historical records from the Super sports catalogs), we might write:
<daml:Class rdf:ID="CurrentProduct"> <rdfs:label>Current Product</rdfs:label> <rdfs:comment>An item currently sold by Super Sports Inc. at the time of query</rdfs:comment> </daml:Class> <daml:Class rdf:ID="DiscontinuedProduct"> <rdfs:label>Discontinued Product</rdfs:label> <rdfs:comment>An item no longer sold by Super Sports Inc. at the time of query</rdfs:comment> <daml:disjointWith rdf:resource="#CurrentProduct"/> </daml:Class>
The key is the daml:disjointWith
property on the second class. Its value is
the first class, thus asserting the two classes can have no instances in common. The
value
of this and similar properties that apply to DAML+OIL classes are known as class
expressions. In this case, the class expression is a very simple one: the URI of the
target class. But DAML+OIL allows us to perform more complex algebra with class expressions,
as we shall see.
The above does not discuss the relationship between these classes and the product
class. We
would want to clarify the matter by stating that all products are either current products
or
discontinued products. The daml:disjointUnionOf
property allows us to express
this:
<daml:Class rdf:ID="Product"> <rdfs:label>Product</rdfs:label> <rdfs:comment>An item sold by Super Sports Inc.</rdfs:comment> <daml:disjointUnionOf parseType="daml:collection"> <daml:Class rdf:ID="CurrentProduct"> <rdfs:label>Current Product</rdfs:label> <rdfs:comment>An item currently sold by Super Sports Inc. at the time of query</rdfs:comment> </daml:Class> <daml:Class rdf:ID="DiscontinuedProduct"> <rdfs:label>Discontinued Product</rdfs:label> <rdfs:comment>An item no longer sold by Super Sports Inc. at the time of query</rdfs:comment> </daml:Class> </daml:disjointUnionOf> </daml:Class>
Which is, of course, equivalent to
<daml:Class rdf:ID="CurrentProduct"> <rdfs:label>Current Product</rdfs:label> <rdfs:comment>An item currently sold by Super Sports Inc. at the time of query</rdfs:comment> </daml:Class> <daml:Class rdf:ID="DiscontinuedProduct"> <rdfs:label>Discontinued Product</rdfs:label> <rdfs:comment>An item no longer sold by Super Sports Inc. at the time of query</rdfs:comment> </daml:Class> <daml:Class rdf:ID="Product"> <rdfs:label>Product</rdfs:label> <rdfs:comment>An item sold by Super Sports Inc.</rdfs:comment> <daml:disjointUnionOf parseType="daml:collection"> <daml:Class rdf:about="#CurrentProduct"/> <daml:Class rdf:about="#DiscontinuedProduct"/> </daml:disjointUnionOf> </daml:Class>
Note that we omitted the daml:disjointWith
property on
CurrentProduct
. We no longer need this property, as each class in a disjoint
union must be disjoint with all the others.
Disjoint unions can involve more than two classes. For instance, if Super Sports needs to model products that are not yet offered in their catalogs, they might add this category as follows:
<daml:Class rdf:ID="Product"> <rdfs:label>Product</rdfs:label> <rdfs:comment>An item sold by Super Sports Inc.</rdfs:comment> <daml:disjointUnionOf parseType="daml:collection"> <daml:Class rdf:ID="CurrentProduct"> <rdfs:label>Current Product</rdfs:label> <rdfs:comment>An item currently sold by Super Sports Inc. at the time of query</rdfs:comment> </daml:Class> <daml:Class rdf:ID="DiscontinuedProduct"> <rdfs:label>Discontinued Product</rdfs:label> <rdfs:comment>An item no longer sold by Super Sports Inc. at the time of query</rdfs:comment> </daml:Class> <daml:Class rdf:ID="UnreleasedProduct"> <rdfs:label>Unreleased Product</rdfs:label> <rdfs:comment>An item under planning or preparation for sale by Super Sports Inc., but not yet on sale at the time of query</rdfs:comment> </daml:Class> </daml:disjointUnionOf> </daml:Class>
And so all products must fall into exactly one of the categories: current, discontinued, or unreleased.
Non-exclusive combinations
One can also express non-exclusive boolean combinations of classes.
<daml:Class rdf:ID="CampingGear"> <rdfs:label>Camping Gear</rdfs:label> <rdfs:comment>An item designed for use while camping</rdfs:comment> </daml:Class> <daml:Class rdf:ID="HikingGear"> <rdfs:label>Hiking Gear</rdfs:label> <rdfs:comment>An item designed for use while hiking</rdfs:comment> </daml:Class> <daml:Class rdf:ID="FamilyProduct"> <rdfs:label>Family Product</rdfs:label> <rdfs:comment>An item designed for family use</rdfs:comment> <daml:unionOf parseType="daml:collection"> <daml:Class rdf:about="#CampingGear"/> <daml:Class rdf:about="#HikingGear"/> </daml:unionOf> </daml:Class>
A product is a family product if and only if it is either hiking gear or camping gear.
A
class expression establishes a set of resources: those which are types of the class
given in
the expression. daml:unionOf
is a standard union of the sets defined by each of
the listed class expressions.
<daml:Class rdf:ID="HikingGear"> <rdfs:label>Hiking Gear</rdfs:label> <rdfs:comment>An item designed for use while hiking</rdfs:comment> </daml:Class> <daml:Class rdf:ID="Footwear"> <rdfs:label>Footwear</rdfs:label> <rdfs:comment>An item worn on the feet</rdfs:comment> </daml:Class> <daml:Class rdf:ID="HikingShoes"> <rdfs:label>Hiking Shoes</rdfs:label> <rdfs:comment>An item worn on the feet while hiking</rdfs:comment> <daml:intersectionOf parseType="daml:collection"> <daml:Class rdf:about="#HikingGear"/> <daml:Class rdf:about="#Footwear"/> </daml:intersectionOf> </daml:Class>
A product is classified as hiking shoes if and only if it is hiking gear and it is also footwear. This is also standard intersection of the sets defined by the listed class expressions.
We mentioned that a class expression can be more than a simple URI reference. Enumerations, which we covered in the last article, and these boolean class expressions can also be used as class expressions.
More Power To Properties
Let's have a look at some of the additional statements DAML+OIL allows us to make about properties.
Inverse properties
Inverse properties are quite common. If A is the father of B, then B is the child of A. The properties "father" and "child" are the inverse of each other. DAML+OIL allows one to declare this systematically, so that you are free to only assert one property, and its inverse is inferred.
<daml:ObjectProperty rdf:ID="gear"> <rdfs:label>gear</rdfs:label> <rdfs:comment>Indicates a product that is used in association with an activity</rdfs:comment> <daml:domain rdf:resource="#Activity"/> <daml:range rdf:resource="#Product"/> </daml:ObjectProperty> <daml:ObjectProperty rdf:ID="usedFor"> <rdfs:label>used for</rdfs:label> <rdfs:comment>Indicates an activity for which a product is designed</rdfs:comment> <daml:inverseOf rdf:resource="#gear"/> <daml:domain rdf:resource="#Product"/> <daml:range rdf:resource="#Activity"/> </daml:ObjectProperty>
Only one of the properties need carry the daml:inverseOf
property, as it is
reflexive. Also note that the range of one property is the domain of its inverse and
vice
versa.
Transitivity
Another important specialization of properties in DAML+OIL is transitivity. For instance,
the ancestor of your ancestor is also your ancestor. There is at least one common
transitive
property built into RDFS: daml:subClassOf
. If class A is a subclass of B, and
class B is a subclass of C, then class A must be a subclass of C. DAML+OIL allows
one to
give this behavior to any object property one wishes. Suppose that we decide to model
the
interest groups relevant to Super Sports customers. (Like all useful ontologies, ours
already begins to expand beyond its initial scopeg.)
<daml:TransitiveProperty rdf:ID="member"> <rdfs:label>member</rdfs:label> <rdfs:comment>Indicates a group which a person or another group has joined</rdfs:comment> </daml:TransitiveProperty> <daml:Class rdf:ID="Person"> <rdfs:label>Person</rdfs:label> <rdfs:comment>An individual human being</rdfs:comment> </daml:Class> <daml:Class rdf:ID="Organization"> <rdfs:label>Organization</rdfs:label> <rdfs:comment>An collection of affiliated human beings</rdfs:comment> </daml:Class> <!-- Instances. Assume appropriate default XML namespace declaration --> <Organization rdf:ID="AmericanCrossCountrySkiers"> <rdfs:label>American Cross Country Skiers (AXCS)</rdfs:label> <rdfs:comment>An association that serves U.S. Master (age 30 and older) cross country skiers with a wide range of education, promotion and communication programs. </rdfs:comment> </Organization> <Organization rdf:ID="BoulderNordicClub"> <rdfs:label>Boulder Nordic Club (BNC)</rdfs:label> <rdfs:comment>A club organized to support cross country skiing in the Boulder area</rdfs:comment> <member rdf:resource="#AmericanCrossCountrySkiers"/> </Organization> <Person rdf:ID="jsmith"> <rdfs:label>Mr. John Smith</rdfs:label> <rdfs:comment>Mr. John Smith is a cross-country skier from Boulder, Colorado</rdfs:comment> <member rdf:resource="#BoulderNordicClub"/> </Person>
If a DAML+OIL agent, given this information, were asked whether John Smith is a member of AXCS, the answer would be "yes", even though this is not directly stated. The affirmative is given by inference from the fact that John is a member of BNC, BNC is a member of AXCS, and "member" is a transitive property. This works regardless of the fact that the transitive property spans different classes (Person and Organization).
Property Restrictions
Finally, we have reached the most fundamental and most radical facility added by DAML+OIL. For many reasons ranging from convenience to access control, one might not directly assert a classification for a resource. DAML+OIL provides property restrictions, which are a way to restrict classes to a set of resources based on particular properties of theirs, the number of these properties that are asserted, or the value of these properties. The easiest way to consider property restrictions is by example.
<daml:Class rdf:ID="MensProduct"> <rdfs:label>Men's Product</rdfs:label> <rdfs:comment>A product particularly designed to be used by men</rdfs:comment> <rdfs:subClassOf> <daml:Restriction> <daml:onProperty rdf:resource="#targetSex"/> <daml:hasValue rdf:resource="#Male"/> </daml:Restriction> </rdfs:subClassOf> </daml:Class>
This defines the MensProduct
class as a subclass of another class defined
in-line as a DAML+OIL restriction. These special classes are defined by rules that
specify
what conditions of a resources properties must be met for that resource to be a member
of
the class. daml:onProperty
identifies which property is to be checked.
daml:hasValue
then declares that the property in question must have a
particular value. So, in effect, the above says that a men's product is a subclass
of all
resources which have at least one targetSex
property whose value is
Male
.
Of course, because MensProduct
is a subclass of this restriction, there could
be resources that meet this restriction but are not in the MensProduct
class.
To assert that a men's product is any resource that meets this restriction, one would
write:
<daml:Class rdf:ID="MensProduct"> <rdfs:label>Men's Product</rdfs:label> <rdfs:comment>A product particularly designed to be used by men</rdfs:comment> <daml:sameClassAs> <daml:Restriction> <daml:onProperty rdf:resource="#targetSex"/> <daml:hasValue rdf:resource="#Male"/> </daml:Restriction> </daml:sameClassAs> </daml:Class>
daml:hasClass
You can also define property restrictions by the class of the values of a property, rather than its value.
<daml:Class rdf:ID="Ball"> <rdfs:label>Ball</rdfs:label> <rdfs:comment>A ball designed to be used in sports</rdfs:comment> <rdfs:subClassOf rdf:resource="#Product"/> </daml:Class> <daml:Class rdf:ID="BallSport"> <rdfs:label>Ball Sport</rdfs:label> <rdfs:comment>Activities that involve play using balls</rdfs:comment> <daml:sameClassAs> <daml:Restriction> <daml:onProperty rdf:resource="#gear"/> <daml:hasClass rdf:resource="#Ball"/> </daml:Restriction> </daml:sameClassAs> </daml:Class>
Which defines a ball sport as a thing which counts among its gear at least one product
classified as a ball. Remember that gear
is a property we defined as the
inverse of usedFor
, and its domain is fixed as Activity
. This
means that all ball sports must also be activities, since they must have a gear
property, and the domain of this property is Activity
.
But we can also express more clearly the fact that all ball sports are activities.
<daml:Class rdf:ID="BallSport"> <rdfs:label>Ball Sport</rdfs:label> <rdfs:comment>Activities that involve play using balls</rdfs:comment> <rdfs:subClassOf rdf:resource="#Activity"/> <rdfs:subClassOf> <daml:Restriction> <daml:onProperty rdf:resource="#gear"/> <daml:hasClass rdf:resource="#Ball"/> </daml:Restriction> </rdfs:subClassOf> </daml:Class>
This says that a ball sport is a subclass of activities and a subclass of those things that count at least one ball among their gear. Therefore, all ball sports must be activities and must meet this restriction; but note that as stated above, it is possible for an activity to have a ball as gear and yet not be a ball sport. We can eliminate this loophole by rewriting to:
<daml:Class rdf:ID="BallSport"> <rdfs:label>Ball Sport</rdfs:label> <rdfs:comment>Activities that involve play using balls</rdfs:comment> <daml:intersectionOf parseType="daml:collection"> <daml:Class rdf:resource="#Activity"/> <daml:Restriction> <daml:onProperty rdf:resource="#gear"/> <daml:hasClass rdf:resource="#Ball"/> </daml:Restriction> </daml:intersectionOf> </daml:Class>
We can do this because daml:Restriction
is a valid class expression, which
opens it up for use in many DAML+OIL constructs.
daml:toClass
There is another type of property restriction that considers the class of property
values.
daml:toClass
makes a requirement that all the property values for a
resource be of a certain class.
<daml:Class rdf:ID="ObsoleteActivity"> <rdfs:label>Obsolete activity</rdfs:label> <rdfs:comment>Activities for which all related products have been discontinued</rdfs:comment> <daml:intersectionOf parseType="daml:collection"> <daml:Class rdf:resource="#Activity"/> <daml:Restriction> <daml:onProperty rdf:resource="#gear"/> <daml:toClass rdf:resource="#DiscontinuedProduct"/> </daml:Restriction> </daml:intersectionOf> </daml:Class>
So if an activity has any gear that is not a discontinued product, it is not considered
an
obsolete activity. One must be careful with daml:toClass
. Because of the
details of its semantics, a resource will actually meet such a restriction if it does
not
have the property given daml:onProperty
at all. If you're not careful, you
could have a resource unexpectedly meet a daml:toClass
restriction if the
property in question is optional. This is especially treacherous because on the face
of it,
daml:toClass
is more restrictive than daml:hasClass
, and yet the
latter will not allow a resource that is missing the property in question. For this
reason,
unless it is usual for a resource to have more than one instance of a particular property,
it is probably safer to use daml:hasClass
for restrictions according to the
class of the property's value.
Bringing It All Together
Here is an update of the Super Sports ontology, using the DAML+OIL features introduced in this article.
More To Learn
In the first two articles of this series, we have presented the basics of DAML+OIL by example. There are additional property restrictions based on the cardinality (number of occurrences) of a property for each instance, and there are many nuances we have not covered. DAML+OIL introduces many constructs, and at first it can be a bit vexing to try to remember all the different constructs from RDF, RDFS, and DAML+OIL. The final article will provide some assistance by tabulating all these constructs, noting which specification defines them, and briefly describing their usage.