Bringing together the RDF and OO models in the Semantic Web

The RDF model has many similarities to the object-oriented model. These are described in A Semantic Web Primer for Object-Oriented Software Developers:

Domain models consist of classes, properties and instances (individuals). Classes can be arranged in a subclass hierarchy with inheritance. Properties can take objects or primitive values (literals) as values.

The same document states that some of the differences are that RDF is theoretically based on logic, a property in the RDF model is the “first-class citizen”, while in the OO (object-oriented) model it’s defined in the context of a class. The RDF model does not have methods and unlike in the OO model, all parts of the RDF graph are public.

Despite the differences, the basic ideas the OO and RDF models are based on are similar. Objects (resources) are described by properties and relations to other objects and form a graph. One can make an analogy between an object and a URI reference, a primitive data type and a URI reference linked to a (literal) value. This comparison helps understanding not just Hypernotation but the RDF model in general as well.

Hypernotation is most easily understood as an OO model applied to the Web. The Web can be seen as a global root object, while all other objects (resources) are properties of the Web or its children’s objects. These resources are represented by hypernodes, i.e. special types of web resources. Hypernotation sets the rules for publishing hypernodes and linking them into a consistent system of the Web of data.

Dot notation is popular in object-oriented programming languages as an intuitive way of representing and manipulating variables. Dot notation enables unique names/paths representing a location in hierarchy. Hypernotation applies the idea of dot notation to the global context, using URIs as paths for Web “variables”. (where “/” (slash) is used as delimiter instead of “.” (dot)).

On the other hand, it’s based on the RDF model, so the paths actually encode all kinds of relations between the variables, and every data structure can be decomposed to triples.

In this context, hypernodes are just variables, with a difference that the value of a variable is also a hypernode. Types of hypernodes–hyperobjects, hyperarrays and hyperreferences reflect the similar ideas from programing languages. Let’s remind ourselves of the example that I have had used in this blog so far.

Here a person (hyperobject) called Chuck Norris is described with a few properties/relations. You can see the structure that is basically a tree where the nodes represent his name, location and friends. Used terminology is “defined” in the prefix_ hyperarray, whose elements point to different vocabularies.

The names of nodes are URIs, obtained by concatenating all preceeding segments in one branch, similarly as in dot notation. For instance, the URI of the hypernode chuck is http://chucknorris.com/data_/chuck. This hypernode, together with its child hypernodes describing it, constitutes a hyperobject that can be naturally represented as an object in the OO sense. For instance, in JavaScript, it would look like this:

var chuck = {
        foaf_name : "Carlos Ray Norris",
        foaf_based_near : {
                rdf_type : geo_Point,
                geo_lat : 45.45385,
                geo_long : 10.19273
        },
        foaf_knows : {
                steven : steven,
                bruce : bruce
        }
};

Here, the variable chuck represents the hyperobject http://chucknorris.com/data_/chuck. The variables geo_Point, steven and bruce are also objects.

If you try to evaluate the object chuck in JavaScript, you will get "[object Object]" string back. Not very useful. The equivalent of “evaluating” in Hypernotation is looking up (de-referencing) a hypernode. So what will you get in return if you send the HTTP GET request to http://chucknorris.com/data_/chuck? You’ll get the answer HTTP/1.1 200 OK with the following body:

<ul>
    <li><a href="foaf_name">name</a></li>
    <li><a href="foaf_knows">knows</a></li>
    <li><a href="foaf_based_near">based_near</a></li>
</ul>

As you see, Hypernotation is more verbose than JavaScript in this regard. It follows the REST HATEOAS principle, where links are used in the representation of a resource enabling a user (or an application) to follow for more information, i.e. to “move the application from one state to the next“.

Hypernotation is based on the RDF model, so describing resources is standardized thanks to the self-describing nature of RDF. In other words, you don’t need a documentation that describes properties such as “foaf_knows”, because it is the URI of a hypernode itself which is described according to the same universal principles. Also, Hypernotation uses semantic HTML elements. A link is always <a> and a list is always <ul>. As simple as that.

Let’s back to the JavaScript example. I simply named the variable chuck, but the problem is that we are dealing with the global scope here, and by global I don’t mean the property of the global window variable, but global in the context of the whole Web. Therefore, it’s important for variables to have globally unique names.

The problem of name collisions in JavaScript is solved by using namespaces, where all objects are nested in other objects forming a big object (tree) assigned to a single global variable, reducing the chance of name collisions. But if we imagine that instead of the browser window context we suddenly end up in the Web context with a huge number of global variables, solving the problem by picking a random name hoping that it’s unique is no longer a solution.

Earlier in this post, I said that the Web can be seen as a global root object, while all the other objects (resources) are properties of the Web or its children’s objects. How to represent the variable http://chucknorris.com/data_/chuck in the context of its relation to the global “Web variable” then? Let’s divide the path using the standard Hypernotation “/” delimiter.

http:/ / chucknorris.com / data_ / chuck

We ended up with four nodes where chucknorris.com is a kind of property, meaning "has domain chucknorris.com". This fact can be represented with a strange-looking triple, where the property chucknorris.com is a special in a sense that it's neither CURIE nor the standard URI, but it's still unique (which is important):

<http:/> chucknorris.com <http://chucknorris.com> .
<http://chucknorris.com> data: <http://chucknorris.com/data_/chuck>.

The subject http:/ is the "root" segment common to all variables (hypernodes) names. It's a bit awkward looking, so perhaps it should be renamed to, say, a friendlier web. Also, when representing this path in JavaScript, we must put chucknorris.com in square brackets because it is not a valid variable name (it contains ".").

Therefore, http://chucknorris.com/data_/chuck can be represented in JavaScript as web['chucknorris.com'].data_.chuck. The names of other objects can be written in the similar fashion. In that sense, e.g. the foaf_knows hyperarray would have the following value:

web['chucknorris.com'].data_.chuck.foaf_knows = {
	steven : web['stevenseagal.com'].data_.steven,
	bruce : web['brucelee.com'].data_.bruce
};

Don't be confused by the fact the hyperarray is represented as an object in JavaScript. It is a hash (associative array / dictionary), and in JavaScript a hash and an object are the same thing. You can also think of it as an array:

web['chucknorris.com'].data_.chuck.foaf_knows = [web['stevenseagal.com'].data_.steven, web['brucelee.com'].data_.bruce];

In this case, the URIs would use indexes instead of keys. For instance, instead of http://chucknorris.com/data_/chuck/foaf_knows/steven, the URI would be http://chucknorris.com/data_/chuck/foaf_knows/0.

The key difference between hyperarrays and hyperobjects is that the in hyperobjects keys (properties) are URIs, while in hyperarrays they are strings. These keys (segments) represent different types of tree links–typed links and key links. In the previous post I described this in more details.

In the REST context, the hyperarray http://chucknorris.com/data_/chuck/foaf_knows would be a collection or a list resource, while http://chucknorris.com/data_/chuck/foaf_knows/steven and http://chucknorris.com/data_/chuck/foaf_knows/bruce would be item resources belonging to this collection (with steven and bruce as IDs). In REST, the name of a collection typically ends with a plural noun, while in Hypernotation a hyperarray always ends with a verb (relation).

In Hypernotation, the representation of this hyperarray would look as follows:

<ul>
    <li><a href="bruce">bruce</a></li>
    <li><a href="steven">steven</a></li>
</ul>

What about assignment? In the previous example we had the (hyper)array containing two variables: bruce and steven that are references to the corresponding objects. For instance,

web['chucknorris.com'].data_.chuck.foaf_knows.steven = web['stevenseagal.com']data_.steven;

The hyperreference http://chucknorris.com/data_/chuck/foaf_knows/steven would return the following HTML:

<a href="http://stevenseagal.com/data_/steven">Steven Seagal</a>

Describing the object's property as a reference to another object is represented using triples as follows:

<http://chucknorris.com/data_/chuck>
    foaf:knows <http://chucknorris.com/data_/chuck/foaf_knows/steven> .

               <http://chucknorris.com/data_/chuck/foaf_knows/steven>
                   owl:sameAs <http://stevenseagal.com/data_/steven> .

Now let's take a look of the property that is a primitive data type. The property foaf_name is one example. In JavaScript it looks like this:

web['chucknorris.com'].data_.chuck.foaf_name = "Chuck Norris";

Analogously, in Hypernotation the hypernode http://chucknorris.com/data_/chuck/foaf_name will return a string:

Chuck Norris

Finally, the object chuck from the beginning of the post can be fully decomposed to triples in the following way.

<http://chucknorris.com/data_/chuck>
    foaf:name <http://chucknorris.com/data_/chuck/foaf_name> ;
    foaf:based_near <http://chucknorris.com/data_/chuck/foaf_based_near>;
    foaf:knows
        <http://chucknorris.com/data_/chuck/foaf_knows/steven>,
        <http://chucknorris.com/data_/chuck/foaf_knows/bruce> .

<http://chucknorris.com/data_/chuck/foaf_name>
    rdf:value "Chuck Norris" .

<http://chucknorris.com/data_/chuck/foaf_based_near>
    rdf:type <http://chucknorris.com/data_/chuck/foaf_based_near/rdf_type> ;
    geo:lat <http://chucknorris.com/data_/chuck/foaf_based_near/geo_lat> ;
    geo:long <http://chucknorris.com/data_/chuck/foaf_based_near/geo_long> .

<http://chucknorris.com/data_/chuck/foaf_based_near/rdf_type>
    owl:sameAs <http://www.w3.org/2003/01/geo/wgs84_pos#Point> .
<http://chucknorris.com/data_/chuck/foaf_based_near/geo_lat>
    rdf:value "45.45385" .
<http://chucknorris.com/data_/chuck/foaf_based_near/geo_long>
    rdf:value "10.19273" .

<http://chucknorris.com/data_/chuck/foaf_knows/steven>
    owl:sameAs <http://stevenseagal.com/data_/steven> .
<http://chucknorris.com/data_/chuck/foaf_knows/bruce>
    owl:sameAs <http://brucelee.com/data_/bruce> .

Using extended CURIE, a syntatic sugar inspired by the flexible chained blank nodes syntax, this can be written much shorter:

<http://chucknorris.com/data_/chuck>
    foaf:name [ rdf:value "Chuck Norris" ] ;
    foaf:based_near [
        rdf:type [ owl:sameAs <http://www.w3.org/2003/01/geo/wgs84_pos#Point> ] ;
        geo:lat [ rdf:value "45.45385" ];
        geo:long [ rdf:value "10.19273" ];
    ]
    foaf:knows [
        steven [ owl:sameAs <http://chucknorris.com/data_/chuck/foaf_knows/steven> ],
        bruce [ owl:sameAs <http://chucknorris.com/data_/chuck/foaf_knows/bruce> ].
    ]

The property rdf:value has a special role, i.e. it's always used for connecting hyperobjects to literal values. If the object is a literal, the property must be rdf:value, so this doesn't need to be written explicitly. Although theoretically owl:sameAs can be removed as well, we are not going to go there right now. For now, let's just write owl:sameAs using a friendlier CURIE is:. Therefore, the example can be even shorter/friendlier:

<http://chucknorris.com/data_/chuck>
    foaf:name "Chuck Norris" ;
    foaf:based_near [
        rdf:type [ is: <http://www.w3.org/2003/01/geo/wgs84_pos#Point> ];
        geo:lat "45.45385";
        geo:long "10.19273"
    ]
    foaf:knows [
        steven [ is: <http://chucknorris.com/data_/chuck/foaf_knows/steven> ],
        bruce [ is: <http://chucknorris.com/data_/chuck/foaf_knows/bruce> ]
    ]

Now the RDF syntax has almost the same structure as the example using object literal notation written in JavaScript. Finally, let's write the full example, using the global root variable and prefix definitions as well:

<http:/> chucknorris.com [
        prefix: [
                is [ is: <http://www.w3.org/2002/07/owl#sameAs> ],
                rdf [ is: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> ],
                foaf [ is: <http://xmlns.com/foaf/0.1/> ],
                geo [ is: <http://www.w3.org/2003/01/geo/wgs84_pos#> ]
        ]
        data::chuck [
            foaf:name "Chuck Norris" ;
            foaf:based_near [
                rdf:type <http://www.w3.org/2003/01/geo/wgs84_pos#Point> ;
                geo:lat "45.45385";
                geo:long "10.19273"
            ]
            foaf:knows [
               steven [ is: <http://chucknorris.com/data_/chuck/foaf_knows/steven> ],
               bruce [ is: <http://chucknorris.com/data_/chuck/foaf_knows/bruce> ]
            ]
        ]
]

Voila! The Web has a domain chucknorris.com that defines a concept chuck that has a name with a value "Chuck Norris" and a bunch of other properties, which prefixes are all nicely mapped in the prefix_ array. In other words, the object is fully comprised of triples.

Of course, you can say that the price is too high: one must use a lot more triples, together with dealing with rdf:value and owl:sameAs all the time. The elegancy of RDF is lost! That argument doesn't sound unreasonable. However, it think it's just the result of the false perception.

The real nature of triples is that they are building blocks. They are the lowest level of the abstraction of connecting (linking) data. Put in this perspective, complaining about the fact that triples are not elegant for work is similar to complaining that assembly language is not elegant for programming.

Or imagine a human language where everything is decomposed to a large number of the most basic sentences. Would that be elegant? Like we use more complex constructs in a language, we find "higher-level" data structures more intuitive and practical than triples.


  • http://milicicvuk.com/blog/2012/01/14/hypernotation-classification-of-hypernodes/ Hypernotation: Classification of hyperNodes

    [...] = "twitter,facebook,delicious,google_plusone,digg,hackernews,favorites,email,print";In the previous post I discussed how RDF and Object-oriented model can happily live together. In this post, I am going [...]