Liking cljdoc? Tell your friends :D

NaturalLexicon logo ont-app/vocabulary

Integration between Clojure keywords and URIs, plus support for RDF-style language-tagged literals.

Contents

Installation

Available at clojars.

Clojars Project

 (defproject .....
  :dependencies 
  [...
   [ont-app/vocabulary "0.1.1"]
   ...
   ])

Motivation

Clojure provides for the definition of keywords, which function as identifiers within Clojure code, and serve many useful purposes. These keywords can be interned within specific namespaces to avoid collisions. The role played by these keywords is very similar to the role played by IRIs within the Linked Open Data (LOD) community, which also has a regime for providing namespaces.

Ont-app/vocabulary provides mappings between Clojure namespaces and IRI-based namespaces using declarations within Clojure namespace metadata.

There is also support for a similar arrangement within Clojurescript, though some things are done a little differently given the fact that Clojurescript does not implement namespaces as first-class objects.

These mappings set the stage for using Keyword Identifiers (KWIs) mappable between Clojure code and the larger world through a correspondence with URIs.

Another construct from RDF that may have application more generally in graph-based data is that of a language-tagged literal, which tags strings of natural language with their associated language. For example we could use such tags to express the differing orthographies of "gaol"@en-GB vs. "jail"@en-US. This library defines a custom reader tag lstr for declaring similar language-tagged strings, e.g. #lstr "gaol@en-GB" and #lstr "jail@en-US".

Defining Keyword Identifiers (KWIs) mapped to URI namespaces

(ns ...
 (:require
   ...
   [ont-app.vocabulary.core] 
   ...))

This will load function definitions interned in the vocabulary.core namespace, and also a number of other ns declarations, each dedicated to a commonly occurring namespace in the world of LOD.

Basic namespace metadata

Within standard (JVM-based) clojure, the minimal specification to support ont-app/vocabulary functionality for a given namespace requires metadata specification as follows:

(ns org.example
  {
    :vann/preferredNamespacePrefix "eg"
    :vann/preferredNamespaceUri "http://example.org/"
  }
  (:require 
  [ont-app.vocabulary.core :as v]
  ...))

This expresses an equivalence between the clojure keyword...

  :eg/example-var

... and the IRI ...

 <http://example.org/example-var>

The vann prefix refers to an existing public vocabulary which will be explained in more detail below.

Unfortunately, Clojurescript does not implement namespaces as first-class objects, and so there is no ns object to which we can attach metadata. So ont-app/vocabulary provides this idiom to achieve the same effect in both clj and cljs environments:

(voc/cljc-put-ns-meta!
 'org.example
  {
    :vann/preferredNamespacePrefix "eg"
    :vann/preferredNamespaceUri "http://example.org/"
  })

In Clojure, it simply updates the metadata of the named namespace (which may need to be created with create-ns). In Clojurescript, this updates a dedicated map from org.example to 'pseudo-metadata' in a global atom called cljs-ns-metadata.

Working with KWIs

We can get the IRI string associated with a keyword:

iri-for and uri-for

> (v/iri-for :eg/Example)
"http://example.org/Example"
>

The function iri-for works as well for aliases interned in the local lexical environment (note the double-colon):

> (v/iri-for ::v/appendix)
"http://rdf.naturallexicon.org/ont-app/vocabulary/appendix"
>

However, it's important to note that while ::v/appendix and :voc/appendix resolve to the same IRI string, the keywords themselves are not equal in Clojure.

This function is called iri-for because any UTF-8 characters can be used, making them IRIs, a superset of URIs, which use a more limited set of characters. But people are in the habit of using the term URI, so the iri-for function has an alias uri-for.

qname-for

We can get the qname for a keyword:

> (v/qname-for :foaf/homepage)
"foaf:homepage"
>
;; (with the 'v' declaration above)...
> (v/qname-for ::v/appendix)
"voc:appendix"
>

keyword-for

We can get a keyword for an IRI...

> (v/keyword-for "http://xmlns.com/foaf/0.1/homepage")
:foaf/homepage
>

If the namespace does not have sufficient metadata to create a namespaced keyword, the entire URI will be rendered, with reader-friendly characters substituted as needed:

> (v/keyword-for "http://example.com/my/stuff")
:http+58++47++47+example.com+47+my+47+stuff))
>

There is an optional arity-2 version whose first argument is called when no ns could be resolved:

> (v/keyword-for (fn [u k] (log/warn "No namespace metadata found for " u) k)
                  "http://example.com/my/stuff")
WARN: No namespace metadata found for "http://example.com/my/stuff"
:http+58++47++47+example.com+47+my+47+stuff
>          

Accessing namespace metadata

put-ns-meta! and get-ns-meta

Let's take another look at the metadata we used above to declare mappings between clojure namespaces and RDF namespaces:

(v/put-ns-meta!
 'org.example
  {
    :vann/preferredNamespacePrefix "eg"
    :vann/preferredNamespaceUri "http://example.org/"
  })

Note that the metadata for this module includes some qualified keywords in this format:

:<prefix>/<name>

The relations preferredNamespaceUri and preferredNamespacePrefix are part of the public VANN vocabulary, with well-defined usage and semantics.

The namespace for vann is also declared as ont-app.vocabulary.vann in the ont_app/vocabulary/core.cljc file, with this declaration:

(v/put-ns-meta!
 'ont-app.vocabulary.vann
 {
   :rdfs/label "VANN"
   :dc/description "A vocabulary for annotating vocabulary descriptions"
   :vann/preferredNamespaceUri "http://purl.org/vocab/vann"
   :vann/peferredNamespacePrefix "vann"
   :foaf/homepage "http://vocab.org/vann/"
 })

There is an inverse of put-ns-meta! called get-ns-meta:

> (v/get-ns-metadata 'ont-app.vocabulary.foaf)
{
 :dc/title "Friend of a Friend (FOAF) vocabulary"
 :dc/description "The Friend of a Friend (FOAF) RDF vocabulary,
 described using W3C RDF Schema and the Web Ontology Language."
 :vann/preferredNamespaceUri "http://xmlns.com/foaf/0.1/"
 :vann/preferredNamespacePrefix "foaf"
 :foaf/homepage "http://xmlns.com/foaf/spec/"
 :dcat/downloadURL "http://xmlns.com/foaf/spec/index.rdf"
 :voc/appendix [["http://xmlns.com/foaf/spec/index.rdf"
                 :dcat:mediaType "application/rdf+xml"]]
 })
>

These are much richer descriptions than the minimal example in the previous section, with metadata encoded using several different public vocabularies, described below.

Note that these are all simple key/value declarations except the :voc/appendix declaration which is in the form

:voc/appendix [[<subject> <predicate> <object>]....], 

This includes triples which elaborate on constructs mentioned in the key-value paris in the rest of the metadata, in this case describing the media types of files describing the vocabulary which are available for download at the URLs given.

prefix-to-ns

We can get a map all the prefixes of namespaces declared within the current lexical environment:

> (v/prefix-to-ns)
{"dc" #namespace[ont-app.vocabulary.dc],
 "owl" #namespace[ont-app.vocabulary..owl],
 "ontolex" #namespace[ont-app.vocabulary.ontolex],
 "foaf" #namespace[ont-app.vocabulary.foaf],
 ...
 }
 >

In Clojurescript, since there's no ns object, the results would look like this:

> (v/prefix-to-ns)
{"dc" ont-app.vocabulary.dc,
 "owl" ont-app.vocabulary..owl,
 "ontolex" ont-app.vocabulary.ontolex,
 "foaf" ont-app.vocabulary.foaf,
 ...
 }
 >

ns-to-namespace

We can get the IRI namespace associated with an ns

In Clojure:

> (v/ns-to-namespace (find-ns 'ont-app.vocabulary.foaf))
"http://xmlns.com/foaf/0.1/"
>

In both Clojure and ClojureScript:

> (v/ns-to-namespace 'ont-app.vocabulary.foaf)
"http://xmlns.com/foaf/0.1/"
>

namespace-to-ns

We can get a map from namespace URIs to their associated clojure namespaces:

> (v/namespace-to-ns)
{
 "http://www.w3.org/2002/07/owl#"
 #namespace[org.naturallexicon.lod.owl],
 "http://persistence.uni-leipzig.org/nlp2rdf/ontologies/nif-core#"
 #namespace[org.naturallexicon.lod.nif],
 "http://purl.org/dc/elements/1.1/"
 #namespace[org.naturallexicion.lod.dc],
 "http://www.w3.org/ns/dcat#"
 #namespace[org.naturallexicon.lod.dcat],
 ...
 }
>

With the usual allowance for clojurescript described above.

ns-to-prefix

We can get the prefix associated with an ns:

> (v/ns-to-prefix (v/cljc-find-ns 'org.naturallexicon.lod.foaf))
"foaf"
>

clear-caches!

For performance reasons, these metadata values are all cached. If you're making changes to the metadata and it's not 'taking', you may need to clear the caches:

> (v/clear-caches!)

Support for SPARQL queries

RDF is explicitly constructed from IRIs, and there is an intimate relationship between SPARQL queries and RDF namespaces. ont-app/vocabulary provides facilities for extracting SPARQL prefix declarations from queries containing qnames.

sparql-prefixes-for

We can infer the PREFIX declarations appropriate to a SPARQL query:

> (v/sparql-prefixes-for
             "Select * Where{?s foaf:homepage ?homepage}")
("PREFIX foaf: <http://xmlns.com/foaf/0.1/>")
>

prepend-prefix-declarations

Or we can just go ahead and prepend the prefixes...

> (v/prepend-prefix-declarations
               "Select * Where {?s foaf:homepage ?homepage}")
"PREFIX foaf: <http://xmlns.com/foaf/0.1/>
Select * Where{?s foaf:homepage ?homepage}"
>

Common Linked Data namespaces

Part of the vision of the ont-app project is to provide a medium for expressing what adherents to Domain-driven Design and Behavior-driven Design call a "Ubiquitous Vocabulary". It also shares the vision of the Linked Data community that huge network effects can emerge when vocabularies emerge which are shared amongst a community of users working in the same domain.

The is a large number of public vocabularies dedicated to various application domains, some of which have gained a good deal of traction in the Linked Data community. Ont-app/vocabulary includes declarations of their associated namespaces, packaged within the core module, a module dedicated to wikidata, and another dedicated to linguistics.

Imported with ont-app.vocabulary.core

Requiring the ont-app.vocabulary.core module also loads ns declarations dedicated to some of the most commonly used RDF/Linked Open Data prefixes:

PREFIXURIComments
rdfhttps://www.w3.org/2001/sw/wiki/RDFthe basic RDF constructs
rdfshttps://www.w3.org/TR/rdf-schema/expresses class relations, domain, ranges, etc.
owlhttps://www.w3.org/OWL/for more elaborate ontologies
vannhttps://vocab.org/vann/for annotating vocabulary descriptons
dchttp://purl.org/dc/elements/1.1/elements of Dublin Core metadata initiative
dcthttp://purl.org/dc/terms/terms for the Dublin Core metadata initiative
shhttps://www.w3.org/TR/shacl/for defining well-formedness constraints
dcathttps://www.w3.org/TR/vocab-dcat/Data Catalog vocabulary
foafhttp://xmlns.com/foaf/spec/the 'Friend of a Friend' vocabulary
skoshttp://www.w3.org/2004/02/skos/core#for thesaurus-type taxonomies
schema.orghttps://schema.org/mostly commercial topics, with web-page metadata and search-engine indexes in mind

Imported with ont-app.vocabulary.wikidata

Requiring the ont-app.vocabulary.wikidata module imports declarations for the several namespaces pertinent to the Wikidata database.

It also defines the value for Wikidata's public SPARQL endpoint is as the constant:

ont-app.vocabulary.wikidata/sparql-endpoint

Imported with ont-app.vocabulary.linguistics

The ont-app.vocabulary.linguistics module declares namespaces for:

PREFIXURIComments
ontolexhttp://www.w3.org/ns/lemon/ontolex#for encoding lexical data
pmnhttp://premon.fbk.eu/ontology/core#PreMOn - dedicated to describing English verbs
nifhttp://persistence.uni-leipzig.org/nlp2rdf/ontologies/nif-core#Natural Language Interchange Format - for annotating corpora

There are also a set of namespaces particular to my Natural Lexicon project, which are still under development.

Language-tagged strings

RDF entails use of language-tagged strings (e.g. "gaol"@en-GB) when providing natural-language content. Typing this directly in Clojure code is a bit a bit awkward, since the inner quotes would need to be escaped.

To enable this language tag, we must require the namespace:

(require ...
  [ont-app.vocabulary.lstr :refer [lang]]
  )

This library defines a reader macro #lstr and accompanying record LangStr to facilitate wriing language-tagged strings in clojure. The value above for example would be written: #lstr "gaol@en-GB".

The reader encodes and instance of type LangStr (it is autoiconic):

> (def brit-jail #lstr "gaol@en-GB")
brit-jail
> brit-jail
{:s "gaol", :lang "en-GB"}
> (type brit-jail)
ont_app.vocabulary.lstr.LangStr
> (prn brit-jail)
#lstr "gaol@en-GB"
>

Rendered as a string, the language tag is dropped

> (str #lstr "gaol@en-GB")
"gaol"
>

We get the language tag with lang:

> (lang #lstr "gaol@en-GB")
"en-GB"
>

License

Copyright © 2019-20 Eric D. Scott

Distributed under the Eclipse Public License either version 1.0 or (at your option) any later version.

Natural Lexicon logo

Natural Lexicon logo - Copyright © 2020 Eric D. Scott. Artwork by Athena M. Scott.

Released under Creative Commons Attribution-ShareAlike 4.0 International license. Under the terms of this license, if you display this logo or derivates thereof, you must include an attribution to the original source, with a link to https://github.com/ont-app, or http://ericdscott.com.

Can you improve this documentation? These fine people already did:
Eric & Eric Scott
Edit on GitHub

cljdoc is a website building & hosting documentation for Clojure/Script libraries

× close