When I started work on Clojure-owl the original intention was to provide myself with a more programmatic environment for writing ontologies, where I could work with a full programming language at to define the classes I wanted (n.d.a) After some initial work with functions taking strings, I have moved to an approach where classes (and other ontological entities), are each assigned to a Lisp symbol (n.d.b) I’m using “symbol” rather than “atom” because its a bit more accurate, especially as Clojure uses “atom” with a different meaning.

This means that I now have something which allows me to write ontological terms looking something like this:

(defclass a)
(defclass b :subclass a)

(defoproperty r)
(defclass d
     :subclass (some r b))

While this is quite nice, and looks fairly close to Manchester syntax (n.d.c/) ultimately, so far all this really provides me with is a slightly complex mechanism for achieving what I could already do; which raises the questions, why not just use Manchester syntax? Why bother with the Lisp if this is all I am to achieve?

I think I have now got to the point where the advantages are starting to show through, as I have started to create useful macros, which operate at a slightly higher level of abstraction from Manchester syntax. I will explain this using examples, perhaps inevitably, based around pizza (n.d.d/) which I have started to develop using Clojure-owl.

First I wanted to be able to define several classes at once, rather than having to use a somewhat long-winded defclass form for each; for this I have written a macro called declare-classes — perhaps a slight misnomer, as it also adds the classes to the ontology. This example shows the purpose:

  (declare-classes
   GoatsCheeseTopping
   GorgonzolaTopping
   MozzarellaTopping
   ParmesanTopping)

In practice, this may not be that useful for an ontology builder, as it creates a bare class; no documentation, nothing else. It may be useful for forward-declaration (like Clojure declare).

One slightly unfortunate consequence of the decision to use lisp symbols is I know find myself writing a lot of macros. For those who have not used lisp before, most work is done with functions. Macros are only necessary when you wish to extend the language itself. They tend to be more complex to write and to debug, although fortunately are easy to use. Compare, for example, the definition of declare-classes to that of the functional equivalent which uses strings.

(defmacro declare-classes
  [& names]
  `(do ~@(map
          (fn [x#]
            `(defclass ~x#))
          names)))

(defun f-declare-classes
  [& names]
  (dorun
   (map #(owlclass x) names)))

Even in this case, there is more hieroglyphics in the macro — two backticks, one unquote splice and some gensym symbols although Clojure’s slightly irritating lazy sequences and the resultant dorun mean that the two are nearly as long as each other. I suspect that the macros are going to get more complex, however. In most cases, should not be the user of the library that has to cope though.

While this provided a useful convenience, I also wanted a cleaner method for declaring disjoints. Consider this example:

(defclass a)
(defclass b)
(defclass c)

(disjointclasses a b c)

This is reasonably effective, but a pain if there are many classes, as they all need to be listed in the disjointclasses list. Worse, this is error prone; it is all too easy to miss a single class out, particularly if a new classes is added. So, I have now implemented an as-disjoint macro which gives this code:

(as-disjoints
   (defclass a)
   (defclass b)
   (defclass c))

This should avoid both the risk of dropping a disjoint, as well avoiding the duplication. An even more common from is to wish to declare a set of classes as disjoint children. Again, I have provided a macro for this, which looks like this:

 (defclass CheeseTopping)

 (as-disjoint-subclasses
  CheeseTopping

  (declare-classes
   GoatsCheeseTopping
   GorgonzolaTopping
   MozzarellaTopping
   ParmesanTopping))

Although this was not my original intention, these are actually nestable. This gives the interesting side effect that the ontology hierarchy is now represented in the structure of the lisp. Example below is an elided hierarchy from pizza. Lisp programmers will notice I have rather exaggerated the indentation to make the point.

(as-disjoint-subclasses
 PizzaTopping

    (defclass CheeseTopping)

    (as-disjoint-subclasses
         CheeseTopping

        (declare-classes
            GoatsCheeseTopping))

    (defclass FishTopping)
    (as-disjoint-subclasses
        FishTopping

        (declare-classes AnchoviesTopping))

    (defclass FruitTopping)
    (as-disjoint-subclasses
         FruitTopping

         (declare-classes PineappleTopping)))

Of course, it is not essential to do this. The nested use of as-disjoint-subclasses confers no semantics; but it does allow juxtaposition of a class and it’s children.

Being able to build up macros in this way was the main reason I wanted a real programming language; those described here are, I think, fairly general purpose; so, this form of declaration could also be supported in any of the various syntaxes, although it would require update to the tools. However, some ontologies will benefit from less general purpose extensions. These are never going to be supported in syntax specification.

Still, it is not all advantage. Using a programming language means embedding within this language. And this means that some of names I would like to use are gone; http://clojuredocs.org/clojure_core/clojure.core/some [some] is the obvious example. While Clojure has good namespace support, functions in clojure.core are available in all other namespaces; like all lisps, Clojure lacks types which would have avoided the problem. There are other ways around this, but ultimately clashing with these names is likely to bring pain; for example, I could always explicitly reference clojure-owl functions; but writing owl.owl.defclass rather than defclass seems a poor option; hence, some has become owlsome, and comment has become owlcomment. I have decided to accept the lack of consistency and kept only and label; the alternative, taken by the OWL API to appending OWL to everything seems too unwieldy.