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:
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.
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" ] .
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.
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:
Let’s walk through the desugaring step by step.
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 >> .
rdf:reifies tripleInternally, 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.
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.(_:b, foaf:name, "Alice") is represented as a triple term.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 .
RDF 1.2 brings syntactic improvements that make reification more expressive and significantly more ergonomic. The new reification syntax:
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.