Liking cljdoc? Tell your friends :D

NaturalLexicon logo ont-app/vocabulary

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

This library should work under both clojure and clojurescript.

Contents

Installation

Available at clojars.

Clojars Project

At which see the declarations for your favorite build tool.

A brief synopsis

Metadata describing RDF prefixing and URIs are added to namespaces (ClojureScript has to be done a little differently) ...

(ns my.example
 {:vann/preferredNamespacePrefix "eg"
  :vann/preferredNamespaceUri "http://example.com/"}
...
)

Then the metadata is referenced to render resources in various forms...

(ns my.application
 (:require
   [my.example yadda yadda]
   [ont-app.vocabulary.core :as voc]))

> (voc/as-kwi "http://example.com/Foo")
:eg/Foo

> (voc/as-uri-string :eg/Foo)
"http://example.com/Foo"

> (voc/as-qname :eg/Foo
"eg:Foo"

> (voc/resource= :eg/Foo "http://example.com/Foo")
true

Also, metadata for a number of commonly used public vocabularies are imported automatically the ont-app.voc.core module:

> (voc/as-uri-string :rdfs/label)
"http://www.w3.org/2000/01/rdf-schema#label"

This works for all the typical URI schemes, (and can be extended as needed):

> (ns tmp
   {:vann/preferredNamespacePrefix "tmp"
    :vann/preferredNamespaceUri "file://tmp/"
    })

> (def my-temp-file (clojure.java.io/file "/tmp/my-file.txt"))

> (voc/as-uri-string  my-temp-file)
"file://tmp/my-file.txt"

> (voc/as-kwi my-temp-file)
:tmp/myfile.txt

> (voc/as-qname my-temp-file)
"tmp:myfile.txt"

There's a multimethod for minting unique keyword identifiers:

> (mint-kwi :eg/widget {... :part-number 123 ...})
:eg/widget#partNumber=123

RDF-style language tags and typed literals are also supported:

(def my-thing {:rdfs/label #{"my thing@en" "meine Sache@de "mi cosa@es" "我的东西@zh"}
               :eg/height "2.3^^unit:Meter})

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 URIs within the Linked Open Data (LOD) community, which also has a regime for providing namespaces.

Ont-app/vocabulary provides mappings between Clojure namespaces and URI-based namespaces using declarations within their Clojure metadata. It also lets you attach the same metadata to Clojure vars with the same effect.

There is support for a similar arrangement within Clojurescript, though some things are done a little differently since Clojurescript does not implement metadata in the same way.

These mappings set the stage for using Keyword Identifiers (KWIs) mappable between Clojure code and the wider world through a correspondence of URIs within shared public vocabularies.

Another construct from RDF that may have application more generally 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 voc/lstr for declaring similar language-tagged strings, e.g. #voc/lstr "gaol@en-GB" and #voc/lstr "jail@en-US".

There is a similar arrangement for typed literals using the #voc/dstr tag, e.g. #voc/dstr "1^^unit:Meter".

Defining Keyword Identifiers (KWIs) mapped to URI namespaces

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

This will load function definitions interned in the ont-app.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 voc]
  ...))

This expresses an equivalence between the clojure keyword...

  :eg/example-var

... and the URI ...

 <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/put-ns-meta!
 'org.example
  {
    :vann/preferredNamespacePrefix "eg"
    :vann/preferredNamespaceUri "http://example.org/"
  })

In Clojure, it simply updates the metadata of the named namespace. If the namespace does not already exist, it will be automatically 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.

Adding vann metadata to a Clojure Var

On the JVM, You also have the option of assigning the vann metadata described above to a Clojure Var.

(def
  ^{
      :vann/preferredNamespacePrefix "myVar"
      :vann/preferredNamespaceUri "http://example.org/myVar/"
    }
   my-var nil)

This metadata is attached to the var.

(meta #'my.namespace/my-var)
->
{:vann/preferredNamespacePrefix "myVar",
 :vann/preferredNamespaceUri "http://example.org/myVar/",
 ...
 :name my-var,
 :ns #namespace[my.namespace]}}

All the same behaviors described herein for namespace metadata will apply.

Working with URI strings, KWIs, and qnames

Resources for which the resource-type can be derived can be rendered as URI strings, KWIs, and qnames using the functions described in the following sections.

as-uri-string

This maps instances of the resource type to a URI string.

> (voc/as-uri-string :rdfs/label)
"http://www.w3.org/2000/01/rdf-schema#label"
>
> (voc/as-uri-string (clojure.java.io/file "/tmp/example.txt"))
"file://tmp/example.txt"
URI syntax

The system maintains a spec :voc/uri-str-spec which will enforce what it considers well-formed URI strings.

Ordinary URI strings match #"^(http:|https:|file:|urn:|tel:|mailto:|jdbc:|odbc:|ftp:|geo:|git:|gopher:|pop:|telnet:).". You can extend this by configuring the value of (-> @config ::voc/special-uri-str-re), which defaults to `#"^(arn:).".

You may find this a useful reference: https://www.iana.org/assignments/uri-schemes/uri-schemes.xhtml.

as-kwi

This method maps instances of the resource type to a KeyWord Identifier (KWI). This will be a qualfied keyword whose namespace is the prefix declared in vann metadata.

(voc/as-kwi "http://www.w3.org/2000/01/rdf-schema#label")
:rdfs/label

If no metadata has been declared, the keyword is rendered without a namespace, escaped to be reader-friendly.

< (voc/as-kwi "file://my-dir/example.txt")
:file:%2F%2Fmy-dir%2Fexample.txt
>
> (voc/as-uri-string :file:%2F%2Fmy-dir%2Fexample.txt)
"file://my-dir/example.txt"

as-qname

This method maps instances of the resource type to a compact URI string embeddable in many RDF formats. Where possible this will use the prefixes declared in vann metadata, but failing that it will fall back on a URI enclosed in angle brackets. Common parlance calls these "qnames" (an XML legacy), but they are actually more properly called "CURIEs".

(voc/as-qname "http://www.w3.org/2000/01/rdf-schema#label")
"rdfs:label"

If no metadata has been declared, the qname will be rendered as the URI string in angle-brackets.

(voc/as-qname :file:%2F%2Fmy-dir%2Fexample.txt)
"<file://my-dir/example.txt>"

resource=

Returns a truthy value when two different resources map to the same URI, regardless of datatype.

(voc/resource= "http://www.w3.org/2000/01/rdf-schema#label" "rdfs:label")
true

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:

(voc/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:

(voc/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/preferredNamespacePrefix "vann"
   :foaf/homepage "http://vocab.org/vann/"
 })

Using the put-ns-meta! function ensures that this metadata works on both clojure and clojurescript.

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

> (voc/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 pairs in the rest of the metadata, in this case describing the media type of the file describing the foaf specification. This vector-of-triples format is readable by one of ont-app/vocabulary's siblings, ont-app/igraph.

prefix-to-ns

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

> (voc/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:

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

prefix-to-namespace-uri

This returns the :vann/preferredNamespaceUri associated with prefix.

> (voc/prefix-to-namespace-uri "eg")
"http://example.com/"

ns-to-namespace

We can get the URI namespace associated with an ns

In Clojure:

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

In both Clojure and ClojureScript:

> (voc/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:

> (voc/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.naturallexicon.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:

> (voc/ns-to-prefix (voc/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:

> (voc/clear-caches!)

Prefix collisions

There may be cases where two namespaces lay claim to the same prefix. In such cases the disambiguate-prefix-ns method will be called. By default, this will throw an ExceptionInfo of :type ::voc/DuplicatePrefix.

A bit of research into prefixes claimed by existing public vocabularies can help avoid this problem. The website https://prefix.cc/ maintains a registry.

If you encounter this problem, one solution to this would be to voc/put-ns-meta! to overwrite the metadata for one of the namespaces to specify a different prefix, but that might not always be feasible, for example in cases where someone's KWIs already presume a certain prefix mapping.

In such cases you can define your own voc/disambiguate-prefix-ns method, dispatched on (namespace kw). It must return a suitable argument to voc/ns-to-prefix...

(defmethod voc/disambiguate-prefix-ns "data"
  [kw _contending-namespaces]
  (if (#{"foo" "bar"} (name kw))
      (find-ns 'joe.blow.data)
      ;; else
      (find-ns 'jane.blane.data)))

Resource types

The as-uri-string, as-kwi, as-qname and resource= methods are each despatched on the resource-type multimethod. The value returned by this method is a vector of two values, one of which is keyword naming a resource type context. Keying methods to a named context allows us to use the Clojure taxonomy to add layers of nuance to our inference of resource types in different applications.

The resource-type multimethod

Each of the methods described above are dispatched on a method (resource-type <value>) -> [ <context> <datatype>].

The operative context may be specified explicitly in the @voc/config atom, or inferred automatically as described in the following sections.

Resource-type contexts

Different application domains may need to make different distinctions between resource types. For example RDF requires that we recognize something called a blank node, and Jena provides special functionality for such nodes. Using a named context as one component of our resource type allows us a good deal of flexibility in providing layers of logic as different supporting libraries come into play.

The default resource-type context is ::voc/resource-type-context. Other context identifiers must derive from it as the root ancestor, as discussed below.

An example of a resource-type method definition

Here's a toy example, which declares a ::acme-empl/EmployeeId resource type in the default resource type context, referring to instances of an Employee record...

> (ns com.example.acme.employees
   {:vann/preferredNamespacePrefix "acme-empl"
    :vann/preferredNamespaceUri "http://rdf.example.com/acme/employees"
    }
    (:require
     ...
     [ont-app.vocabulary.core :as voc]
     ...
     ))

> (defrecord Employee [name employee-id])

> (defmethod voc/resource-type [::voc/resource-type-context Employee]
  [_] ::EmployeeId)

> (defmethod voc/as-uri-string ::EmployeeId
    [this]
    (str "http://rdf.example.com/acme/employees/id=" (:employee-id this)))

> (defmethod voc/as-kwi ::EmployeeId
  [this]
  (voc/as-kwi (voc/as-uri-string this)))

> (def smith (->Employee "George Smith" 42))
{:name "George Smith", :employee-id 42}

> (voc/as-uri-string smith)
"http://rdf.example.com/acme/employees/id=42"

> (voc/as-kwi smith)
:acme-empl/id=42

> (voc/as-qname smith)
"acme-empl:id=42"

> (voc/resource= :acme-empl/id=42 "http://rdf.example.com/acme/employees/id=42")
true

Existing resource types

::voc/resource-type-context declares Resource mappings as follows:

Resourcemaps to resource type
java.lang.String
javascript string
:voc/UriString (conforms to spec :voc/uri-str-spec)
:voc/Qname (conforms to spec :voc/qname-spec)
:voc/NonUriString (any other string)
clojure.lang.Keyword
cljs.core/Keyword
:voc/Kwi (conforms to spec :voc/kwi-spec)
:voc/QualifiedNonKwi (a namespaced keyword that does not conform)
:voc/UnqualifiedKeyword (a keyword with no namespace)
java.io.File:voc/LocalFile

Of the resource class tags defined above, there are as-(uri-string|qname|kwi) and resource= methods defined under ::voc/resource-type-context for the following:

  • :voc/UriString
  • :voc/Qname
  • :voc/Kwi
  • :voc/LocalFile

X inferred from Y resource types

Methods dispatched on the following resource class tags are also defined:

  • :voc/KwiInferredFromUriString
    • Infers the KWI based on the as-uri-string method, and vann metadata
  • :voc/UriStringInferredFromKwi
    • Infers the URI string based on the as-kwi method, and vann metadata

So in the the example above we could have done this:

> (defmethod voc/as-uri-string ::EmployeeId
    [this]
    (str "http://rdf.example.com/acme/employees/id=" (:employee-id this)))

> (derive ::EmployeeId :voc/KwiInferredFromUriString)

Qnames are inferred from KWIs by default, as this is usually a straightforward calculation.

Registering new resource-type contexts

The default resource context can be extended with register-resource-type-context!:

> (ns my-ns ...)
> (voc/register-resource-type-context! ::resource-type-context ::voc/resource-type-context)

This will derive ::my-ns/resource-type-context from ::voc/resource-type-context and enable new methods in my-ns to be dispatched on voc/resource-type [::myns/resource-type-context <some type>], overriding the behavior of voc/resource-type [::voc/resource-type-context <some type>].

The sibling modules ont-app/rdf and ont-app/jena provide examples of this.

voc/most-specific-resource-context

This function takes some parent context as an argument (typically ::voc/resource-context) and descends the ancestry tree to find its leaf. If there is an ambiguity in the tree, the function method voc/preferred-child-resource-context will be called, which defaults to throwing an error.

This is used to set the value of (@voc/config ::inferred-operative-resource-context) in the event that (@voc/config ::operative-resource-context) is not set.

Handling ambiguous contexts

By default, the system will attempt to infer the most specific resource type context in its taxonomy. Cases where there is not a unique lineage of resource-type contexts will trigger an ex-info of :type ::voc/ambiguous-resource-type-context error unless:

  • A value for (@voc/config ::voc/operative-resource-context) has been explicitly set.
  • A voc/preferred-child-resource-context method has been defined for the parent from which the ambiguity arises in the taxonomy.
preferred-child-resource-context

This method with signature [parent children] -> preferred-child allows you to specify the preferred branch in your taxonomy in cases where the taxonomy is ambiguous. It is dispatched on parent...

(defmethod voc/preferred-child-resource-context ::Adam
  [parent children]
  ;; prefer ::Abel to ::Cain
  (or (->> children
       set
       (clojure.set/intersection #{::Abel})
       first)
      (ex-info "Expected ::Abel to be included in the children" {...})))

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.

There are 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 for some of their associated namespaces, packaged within the core module. There is a separate 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, range, 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
voidhttp://rdfs.org/ns/void#Vocabulary of Interlinked Datasets.
qudthttp://qudt.org/schema/qudt/Units, Dimensions and Datatypes vocabulary.
unithttp://qudt.org/vocab/unit#Units module of the QUDT vocabulary.

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 as this 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

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 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 #voc/lstr and accompanying deftype LangStr to facilitate writing language-tagged strings in clojure. The value above for example would be written: #voc/lstr "gaol@en-GB".

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

> (def brit-jail #voc/lstr "gaol@en-GB")
brit-jail

> brit-jail
#voc/lstr "gaol@en-GB"

> (type brit-jail)
ont_app.vocabulary.lstr.LangStr

Rendered as a string, the language tag is dropped

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

We get the language tag with lang:

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

Typed literals

RDF has a regime for representing tagged datatpes analogous to language tags, e.g. "1"^^xsd:integer. The ont-app.vocabulary.dstr module provides support for a similar approach in clojure using #voc/dstr...

> (voc/tag 42)
#voc/dstr "42^^xsd:long"

> (type #voc/dstr "42^^xsd:long")
ont_app.vocabulary.dstr.DatatypeStr

> (str #voc/dstr "42^^xsd:long")
"42"

> (dstr/datatype #voc/dstr "42^^xsd:long")
"xsd:long"

> (voc/as-uri-string (dstr/datatype #voc/dstr "42^^xsd:long"))
"http://www.w3.org/2001/XMLSchema#long"

> (voc/untag #voc/dstr "42^^xsd:long")
42

> (voc/tag (short 42))
#voc/dstr "42^^xsd:short"

> (untag #voc/dstr "42^^xsd:short")
42

> (type *1)
java.lang.Short

> (voc/tag "1.25" :eg/Euros)
#voc/dstr "1.25^^eg:Euros"

The tag multimethod

This takes one or two arguments.

With a second argument we can explicitly specify the datatype resource. The datatype spec will be translated with voc/as-qname...

> (voc/tag 1 :unit/Meter)
#voc/dstr "1^^unit:Meter"

We can omit the second argument if the type of the object is registered in dstr/default-tags ...

> dstr/default-tags
#<Atom@793bab96:
  {clojure.lang.Symbol "clj:Symbol",
   clojure.lang.Var "clj:Var",
   java.lang.Boolean "xsd:Boolean",
   java.lang.Byte "xsd:byte",
   java.lang.Class "clj:JavaClass",
   java.lang.Double "xsd:double",
   java.lang.Float "xsd:float",
   java.lang.Long "xsd:long",
   java.lang.Short "xsd:short",
   java.lang.String "xsd:string",
   java.util.Date "xsd:dateTime"}>

> (voc/tag #inst "2000")
#voc/dstr "2000-01-01T00:00:00Z^^xsd:dateTime"

Here's a possible definition of the method for :xsd/dateTime:

(defmethod tag :xsd/dateTime
  [obj & _]
  (dstr/->DatatypeStr (-> obj (.toInstant) str)
                      "xsd:dateTime"))

The type xsd:decimal is derived from :xsd/double:

> (descendants :xsd/double)
#{:xsd/decimal}

The untag multimethod

This is the inverse of the tag method.

Typically we provide a single DatatypeStr instance, returning a native clojure instance...

> (voc/untag #voc/dstr "2000-01-01T00:00:00Z^^xsd:dateTime")
#inst "2000-01-01T00:00:00.000-00:00"

... with the operative defmethod...

(defmethod untag :xsd/dateTime
  [obj]
  (clojure.instant/read-instant-date (str obj)))

There is an optional second argument to handle cases where there is no untag method for the datatype in question.

With one argument we'll get an error...

> (voc/untag #voc/dstr "2.2^^unit:Meter")
Execution error (ExceptionInfo) ... No untag method found for 2.2^^unit:Meter

... so let's convert into feet...

> (defn meters-to-feet [m] (* 3.280839895 m))
> (voc/untag #voc/dstr "2.2^^qudt:Meter" #(-> % str read-string meters-to-feet))
7.217847769000001

Support for SPARQL queries and Turtle/n3

RDF is explicitly constructed from URIs, 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:

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

turtle-prefixes-for

> (voc/turtle-prefixes-for "eg:SomeGuy foaf:homepage eg:SomeWebPage.")
("@prefix eg: <http://rdf.example.com/>."
 "@prefix foaf: <http://xmlns.com/foaf/0.1/>.")

prepend-prefix-declarations

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

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

SPARQL is the default. Use voc/turtle-prefixes-for for turtle or n3...

> (voc/prepend-prefix-declarations
     voc/turtle-prefixes-for
     "eg:SomeGuy foaf:homepage eg:SomeWebPage.")

"@prefix eg: <http://rdf.example.com/>.\n@prefix foaf: <http://xmlns.com/foaf/0.1/>.\neg:SomeGuy foaf:homepage eg:SomeWebPage."

Minting Identifiers

Sometimes it's nice to have an easy way to mint canonically named unique identifiers.

The mint-kwi method

The mint-kwi method exists for that purpose. Arguments include a 'head' on which the method will be dispatched, followed by any number of other arguments, which should be sufficient to uniquely identify its target.

There is a default:

> (mint-kwi :eg/widget :part-number 123))

:eg/widget_part-number_123

Or you can roll your own:

> (defmethod voc/mint-kwi :eg/widget
  [_ & {:keys [part-number]}]
  (keyword "eg"
           (str "widget#partNumber=" part-number)))

> (mint-kwi :eg/widget :part-number 123)
:eg/widget#partNumber=123
> (voc/as-uri-string *1)
"http://rdf.example.com/widget#partNumber=123"

The kw-string method

A supporting utility method is voc/kw-string with signature [this] -> string, dispatched on (type this), which will generate a string to serve as a substring in your KWI name. It defaults to just return (str this), but a Seqable object will return (str (abs (hash this))), and a keyword will return just the name portion.

> (voc/kw-string (list 1 2 3 {:a 1 :b 2}))
"876627597"

The voc/kw-string method informs the default mint-kwi method:

> (voc/mint-kwi :eg/my-thing :x :y :z (list 1 2 3 {:a 1 :b 2}))
:eg/my-thing_x_y_z_876627597

Configuration

There is a global atom voc/config, holding a map used to configure a few parameters. These are all for advanced features, and already default to reasonable values.

KeyNotes
:operative-resource-context (optional)See section on resource type contexts.
:inferred-operative-resource-context (automatic)Inferred automatically in the absence of an explicit :operative-resource-context. Default is the value of (voc/most-specific-resource-context ::voc/resource-type-context). See section on registering new resource-type contexts
:special-uri-str-reExtends acceptable URI string patterns. See section on URI syntax. Default is #"^(arn:).*",

License

Copyright © 2019-25 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 Scott, Eric & Boris Kourtoukov
Edit on GitHub

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

× close