Exploring Reification Syntax in RDF 1.2 Turtle

RDF 1.2 Turtle introduces several syntactic improvements intended to make data modeling more expressive and concise. One of the most notable enhancements is the updated reification syntax in Turtle, which provides a cleaner way to talk about triples—something long requested by modelers and practitioners.

In this post, we’ll walk through some related pieces of syntax:

  1. Blank node property lists
  2. The new reified triple syntax
  3. The new annotation syntax

Understanding how these work—and how they desugar into standard RDF—gives you a clearer picture of what RDF 1.2 is actually doing under the hood.

Blank node property lists

Turtle has long allowed blank node property lists, i.e. using [...] to conveniently construct a blank node and immediately attach properties.

What’s important (and sometimes overlooked) is how the grammar expands when a blank node property list appears as the subject of a triple.

Consider the following valid Turtle fragment:

PREFIX foaf: <http://xmlns.com/foaf/0.1/>

[ foaf:name "Alice" ] .

Why is this valid?

In the Turtle grammar, the production for triples allows a blankNodePropertyList optionally followed by a predicateObjectList. Because the predicate–object list is optional, a standalone blank node property list can itself constitute a complete triple block. This can be rewritten explicitly as:

PREFIX foaf: <http://xmlns.com/foaf/0.1/>

[] foaf:name "Alice" .

And the fully expanded N‑Triples form is simply:

_:a <http://xmlns.com/foaf/0.1/name> "Alice" .

Turtle’s syntactic sugar doesn’t change the underlying RDF graph—it just helps you write it more concisely.

Reified Triple Syntax in RDF 1.2

RDF 1.2 adds a new syntactic form for reified triples, using the << ... >> notation. This makes it easier to refer to a triple as a first‑class entity within Turtle.

For example:

VERSION "1.2"
PREFIX foaf: <http://xmlns.com/foaf/0.1/>

<< [] foaf:name "Alice" >> .

This raises several questions:

  • What is the identity of the reified triple object?
  • How does nested syntax expand?
  • What is the underlying RDF produced?

Let’s walk through the desugaring step by step.

Step 1: The basic reified triple form

The simplest form:

<< [] foaf:name "Alice" >> .

can also be written using the explicit “tilde” variant without specifying the identity of the reifier:

<< [] foaf:name "Alice" ~ >> .

optionally an IRI or blank node can be assigned to identify the reifier, here with an anonymous blank node:

VERSION "1.2"
PREFIX foaf: <http://xmlns.com/foaf/0.1/>

<< [] foaf:name "Alice" ~ [] >> .

All these are syntactic equivalents.

Such reified triples can be used in the subject or object position of other triples, and also as the subject or object in other reified triples:

VERSION "1.2"
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
PREFIX prov: <http://www.w3.org/ns/prov#>
PREFIX xsd:  <http://www.w3.org/2001/XMLSchema#>

# reified triples as subject and object
<< [] foaf:name "Alice" >> prov:generatedAtTime "2026-01-29T11:35:47Z"^^xsd:dateTime .
[] prov:generated << [] foaf:name "Alice" >> .

# nested reified triples
<< << [] foaf:name "Alice" >> prov:generatedAtTime "2026-01-29T11:35:47Z"^^xsd:dateTime ~ _:rtSubject >> .
<< [] prov:generated << [] foaf:name "Alice" >> ~ _:rtObject >> .

Step 2: Expanding to the rdf:reifies triple

Internally, RDF 1.2 represents reified triples using the rdf:reifies predicate connecting a metadata node to the triple term.

So the above expands to:

VERSION "1.2"
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>

[] rdf:reifies <<( [] foaf:name "Alice" )>> .

The <<( ... )>> around the embedded triple represent the triple term itself.

We can also write the expanded version as:

VERSION "1.2"
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>

[ rdf:reifies <<( [] foaf:name "Alice" )>> ] .

Which directly mirrors the earlier blank node example, but now the object of rdf:reifies is the triple term.

Note: triple terms cannot appear in the subject position of a triple in Turtle.

Final N‑Triples Expansion

Ultimately, all of this corresponds to a single N‑Triples statement:

VERSION "1.2"

_:a <http://www.w3.org/1999/02/22-rdf-syntax-ns#reifies> <<( _:b <http://xmlns.com/foaf/0.1/name> "Alice" )>> .

where:

  • _:a is the metadata node.
  • _:b is the blank node subject of the original triple.
  • The triple (_:b, foaf:name, "Alice") is represented as a triple term.

Annotation Syntax in RDF 1.2

RDF 1.2 also introduces the {| ... |} annotation block syntax. This provides a compact way to attach metadata to an asserted triple.

VERSION "1.2"
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
PREFIX prov: <http://www.w3.org/ns/prov#>
PREFIX xsd:  <http://www.w3.org/2001/XMLSchema#>

[] foaf:name "Alice" {| prov:generatedAtTime "2026-01-29T11:35:47Z"^^xsd:dateTime |} .

The annotation syntax allows the definition of one or more reifiers as either IRIs or blank nodes, each preceded by a tilde (~), which precedes an optional annotation block.

So, we can add a tilde to an asserted statement to assign a reifier:

VERSION "1.2"
PREFIX foaf: <http://xmlns.com/foaf/0.1/>

[] foaf:name "Alice" ~ .

Optionally an IRI or blank node can be assigned to identify the reifier, here with an anonymous blank node:

VERSION "1.2"
PREFIX foaf: <http://xmlns.com/foaf/0.1/>

[] foaf:name "Alice" ~ [] .

This expands to:

VERSION "1.2"
PREFIX foaf: <http://xmlns.com/foaf/0.1/>

[] foaf:name "Alice" .
<< [] foaf:name "Alice" ~ [] >> .

The reified triple can then be further expanded as shown in earlier examples.

It’s also worth to note that the annotation syntax permits multiple reifiers and annotation blocks.

VERSION "1.2"
PREFIX foaf: <http://xmlns.com/foaf/0.1/>

[] foaf:name "Alice" ~~~~ . # assigns four blank node reifiers to the triple

Is equivalent to:

VERSION "1.2"
PREFIX foaf: <http://xmlns.com/foaf/0.1/>

[] foaf:name "Alice" .
<< [] foaf:name "Alice" ~ [] >> .
<< [] foaf:name "Alice" ~ [] >> .
<< [] foaf:name "Alice" ~ [] >> .
<< [] foaf:name "Alice" ~ [] >> .

For each annotation block, any reifier definition that directly precedes the annotation block is used as the subject of the annotations. Thus:

VERSION "1.2"
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
PREFIX prov: <http://www.w3.org/ns/prov#>
PREFIX xsd:  <http://www.w3.org/2001/XMLSchema#>
PREFIX : <http://example.com/>

[] foaf:name "Alice"
    ~ :annotation1 # reifier without annotation block
    ~ :annotation2 {| prov:generatedAtTime "2026-01-29T11:35:47Z"^^xsd:dateTime |} # reifier with annotation block
    {| prov:invalidatedAtTime "2026-01-29T11:35:47Z"^^xsd:dateTime |} . # annotation block without reifier

expands to:

VERSION "1.2"
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
PREFIX prov: <http://www.w3.org/ns/prov#>
PREFIX xsd:  <http://www.w3.org/2001/XMLSchema#>
PREFIX : <http://example.com/>

[] foaf:name "Alice" .
<< [] foaf:name "Alice" ~ :annotation1 >> .
<< [] foaf:name "Alice" ~ :annotation2 >> prov:generatedAtTime "2026-01-29T11:35:47Z"^^xsd:dateTime .
<< [] foaf:name "Alice" ~ [] >> prov:invalidatedAtTime "2026-01-29T11:35:47Z"^^xsd:dateTime .

Conclusion

RDF 1.2 brings syntactic improvements that make reification more expressive and significantly more ergonomic. The new reification syntax:

  • avoids the verbosity of classic rdf:Statement reification,
  • remains fully grounded in RDF semantics,
  • fits naturally with Turtle’s existing syntactic sugars such as blank node property lists.

If you frequently model provenance, annotations, assertions‑about‑assertions, or knowledge graph metadata, this new syntax is a major usability win.

In future posts we’ll look at when to use triple terms and reifying triples and how to query those using the SPARQL 1.2 Query Language.