Menu

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.