Aesopica, Part 2: Literal Values


This article is the second part of a series, examining the use of the Clojure language for representing Linked Data, with examples from Aesop's stories. Part one can be found on this site where the basic elements of the Fox and the Stork story were formalised. In this article we examine how literal values can be represented, using Clojure, in Linked Data. The code to enable the functionality described in these articles can be found in the Aesopica library for using Clojure to write Linked Data.

As mentioned in the previous article the story of the Fox and the Stork is about the fox who invited the stork for a dinner. At the dinner soup was served from a shallow plate that the fox could eat but the stork could not. In return, the stork invited the fox to a dinner, where the food was served in a narrow mouthed jug. This time the fox could not reach the food, while the stork ate it happily.

Elements of this story can be represented as Linked Data, and in particular the Resource Description Framework (RDF) that allows for a precise retelling of the story that is understandable to both humans and machines alike.

For example, the part of the Linked Data we generated in the previous article is as follows:

@base <> .
@prefix rdf: <> .

<fox> rdf:type <animal>.
<stork> rdf:type <animal>.
<fox> <gives-invitation> <invitation1>.
<invitation1> <has-invited> <stork>.
<invitation1> <has-food> <soup>.

which details the elements of the story that the fox invites the stork, where soup is served. Elements that might be implicitly obvious to a human reader, but not to a program, that the fox and the stork are animals, are also represented in this fragment. These elements are describes as a set of facts, where each fact is a triple in the form of a subject, predicate and object. Each part of these facts in this example are represented as a Uniform Resource Identifier (URI), which are shortened with prefixes (i.e. "rdf") or the base URI (i.e. <fox> is a shorthand for ).

Now suppose we want to expand on the elements of this story. For example, we want to give the fox and the stork a name, an age, describe their personalities, give a time for the dinners, etc. For many of these elements we want to simply use value as objects in the representations. For example the number 2 as a representative of the age of the fox. In such scenarios we do not use URIs in the facts but Literals.

Literals can be used to denote numbers, strings, dates and other such elements. In the Linked Data representation below we describe various attributes of the fox, the stork and the dinner with such literals.

@base <> .
  @prefix rdf: <> .
  @prefix foaf: <> .
  @prefix xsd: <>.

  <fox> rdf:type <animal>.
  <fox> foaf:name "vo".
  <fox> foaf:age 2.
  <fox> <is-cunning> true.
  <stork> rdf:type <animal>.
  <stork> foaf:name "ooi".
  <stork> foaf:age 13.
  <stork> <is-cunning> true.
  <dinner1> <has-date> "2006-06-30T20:00:00"^^xsd:dateTime

As one can see in this example, representing literals is very similar to other objects. For example "vo" in the triple <fox> foaf:name "vo" represents the name of the fox. Note that the base of foaf in foaf:name and foaf:age refers to the 'Friend of a Friend' ontology, that allows us to use the common terminology of this ontology to describe facts about the fox and the stork. Literals such as 2 or true describe the age, and whether the fox is cunning, respectively. These are called the lexical forms of the literals and while they also have explicit types (e.g. or simply xsd:string when using prefixes ), these types of literals are so common that writing the types explicitly is not required.

The slightly more complicated case is the definition of the time of the dinner shown by "2006-06-30T20:00:00"^^xsd:dateTime that shows off custom types for literals, or when we would like to give the type explicitly. Here the addition of the ^^xsd:dateTime is an URI (with a prefix) describing how lexical form, i.e. "2006-06-30T20:00:00" exactly maps to a particular value. This allows for easier interpretation of such literal values for machines.

As in the previous article, we aim to use the data representation and manipulation capabilities of Clojure to represent the above-mentioned fragment. Again, for the basic cases, such as strings, numbers, etc, we can be pretty straightforward and only use the lexical form, i.e. "vo", 2 or true in or representations. For the cases where we also want to specify a custom datatype, we use a map such as:

{::aes/value "2006-06-30T20:00:00" ::aes/type :xsd/dateTime}

where the keys ::aes/value and ::aes/type are representing the lexical form and datatype respectively. Note that aes in these keywords, and other refers, to the core namespace of the Aesopica library implementing this data representation. To full Clojure version of this example can be found below:

   {nil ""
    :rdf ""
    :foaf ""
    :xsd ""}
   #{[:fox :rdf/type :animal]
     [:fox :foaf/name "vo"]
     [:fox :foaf/age 2]
     [:fox :is-cunning true]
     [:fox :has-weight 6.8]
     [:stork :rdf/type :animal]
     [:stork :foaf/name "ooi"]
     [:stork :foaf/age 13]
     [:stork :is-cunning true]
     [:dinner1 :has-date {::aes/value "2006-06-30T20:00:00" ::aes/type :xsd/dateTime}]}}

Of course given that this story is centuries old, it is unlikely that the dinner took place at 2006-06-30T20:00:00. As always care must be taken when taking things literally.