=Paper=
{{Paper
|id=Vol-3759/paper13
|storemode=property
|title=Linking application and semantic data with RDF Lens
|pdfUrl=https://ceur-ws.org/Vol-3759/paper13.pdf
|volume=Vol-3759
|authors=Arthur Vercruysse,Julian Rojas,Pieter Colpaert
|dblpUrl=https://dblp.org/rec/conf/i-semantics/VercruysseRC24
}}
==Linking application and semantic data with RDF Lens==
Linking application and semantic data with RDF Lens
Arthur Vercruysse1,* , Julián Rojas Meléndez1,* and Pieter Colpaert1
1
IDLab, Department of Electronics and Information Systems, Ghent University – imec
Abstract
Linked Data is commonly regarded as an unfriendly data structure to be directly used by
application developers. The (often unknown) triple-based structure of RDF graphs causes
developers to struggle to extract the triples of interest and translate them into the object-like
structure needed for their application. A generic, composable and reusable way to look into
RDF graphs as plain objects would remove an important barrier for integrating Linked Data in
all facets of application logic. We propose RDF Lens, a library based on ideas from the Haskell
lens library that allows for composable and reusable data extraction units, called lenses. Value
is shown by implementing a lens that generates a new lens based on a SHACL shape that
extracts the semantic data into the desired plain object. Abstracting data extraction at the
lens level allows for mixed extraction: using both custom extraction and declarative extraction,
which could increase ease of use and reusability. The current implementation is a proof of
concept that defines how to extract data from an RDF graph in JavaScript applications but
does not allow (yet) writing or altering linked data with the same lenses. Future work would
allow for creating and updating Linked Data in the same elegant way.
Keywords
RDF, SHACL, Functional programming, Lenses, JavaScript, Haskell
1. Introduction
When processing RDF data, a developer needs to deal with a list of interlinked triples
that conform to a graph. The promise is that the added complexity of dealing with a
graph can lead to higher interoperability as the IRIs used in named nodes in any position
of the triple can be shared across datasets and systems. The contract between data and
application code is loosened, as developers should not code against specific objects, but
against graph patterns, allowing more systems to be connected.
Coding against such graph patterns is more complex than coding against structured data
(e.g, JSON objects) and may require following non-traditional programming paradigms
to build efficient and reusable code [1]. While applications processing JSON-like objects
already get the data in the right structure, applications processing RDF data need to
write additional logic to create the structures they need to process data further. This
SEMANTiCS’24: International Conference on Semantic Systems, September 17–19, 2024, Amsterdam,
NL
*
Corresponding author.
$ arthur.vercruysse@ugent.be (A. Vercruysse); JulianAndres.RojasMelendez@UGent.be
(J. R. Meléndez); pieter.colpaert@ugent.be (P. Colpaert)
https://pietercolpaert.be (P. Colpaert)
0000-0003-3467-9755 (A. Vercruysse); 0000-0002-6645-1264 (J. R. Meléndez); 0000-0001-6917-2167
(P. Colpaert)
© 2024 Copyright for this paper by its authors. Use permitted under Creative Commons License Attribution 4.0
International (CC BY 4.0).
CEUR
ceur-ws.org
Workshop ISSN 1613-0073
Proceedings
becomes time-consuming and may lead to code duplication across the codebase, as similar
logic might be needed for slightly different cases.
In the JavaScript ecosystem, libraries like Linked Data Objects [2], LDFlex [3], RDF
Object1 , or SimpleRDF2 rely on JSON-LD contexts, which does not allow for type-
checking on all object properties. Another type of library, ts-rdf-mapper3 builds code
that interacts with an RDF dataset based on annotations on the type definitions in
the program itself. This approach is tightly coupled with the code base making it very
difficult to reuse in different applications, compared to reusing the same JSON-LD context
in multiple programs.
We aim to build a framework that allows to compose different pieces of data processing
logic together, potentially reused from earlier tasks, to deliver the structures needed for
an application. In that respect, the functional programming concept of data Lenses [4], as
implemented in the Haskell lens library4 , defines precisely such composable and reusable
data structures. In Haskell, lenses are used to view, update or create (deeply) nested
records. Without lenses, developers need to destructure entire objects and recompose
them after each operation, similarly as it is needed when dealing with complex RDF
graphs.
This paper shows how the idea of Haskell lenses can be applied to RDF. With lenses,
extracting complex objects from a graph becomes easier due to the composability and
typing support that lenses can provide. As the main use case, we show how to derive a
new lens based on a SHACL shape, that in turn can extract objects according to the
shape definition.
In the remainder of this paper, the second section provides a brief description of the
Haskell lens concept. The third section shows how and why we apply the idea of lenses
on RDF graphs. The next section describes the use case where we can derive a lens from
a SHACL shape. The last section presents the conclusion and prospective future work.
2. Haskell lenses
In Haskell, working with structured data entails that for each modification on a nested
value, a developer needs to destructure the object and reconstruct it back after performing
the modification. This leads to unreadable and unmaintainable code. A Haskell lens
aims to facilitate such procedures by lifting an operation over a value, to an operation
over an object, also known as monads applied over data [5].
A lens is built by combining a getter and a setter function over a defined type. With a
lens, is possible to perform operations over a data structure such as [6]:
• Access a subpart (view)
• Alter the whole by modifying a subpart (update)
• Merge the lens with another lens to get a deeper view (composition)
1
https://github.com/rubensworks/rdf-object.js
2
https://github.com/simplerdf/simplerdf
3
https://github.com/artonio/ts-rdf-mapper
4
https://hackage.haskell.org/package/lens
For example, take a data structure representing a Line type composed by a start and
an end Point type. Increasing the x coordinate of the starting Point by one requires
destructuring and reconstructing the entire Line object, as shown in Listing 1. In this
example, a Point only has two coordinates, however, the code complexity increases
quickly with more complex subtypes.
Lenses abstract how to access a nested object away and make it composable. Listing 1
also shows the same function implemented differently using lenses. The over function
applies the (+1) after accessing the field and returns a modified object. That function is
predefined in the Lens library, start and x are lenses generated by the library.
1 data Point = Point { _x :: Double, _y :: Double }
2 data Line = Line { _start :: Point, _end :: Point }
3
4 -- Deconstruct to access the values, return a reconstructed object
5 shiftStartByOne (Line (Point x y) end) = Line (Point (x + 1) y) end
6 -- `over` applies a function over a value after accessing the value using the lens
7 shiftStartByOne' = over (start . x) (+ 1)
Listing 1: Type declaration of a Line and a function that increases the x coordinate by
one, implemented naively and with a Lens.
3. RDF Lens
RDF Lens5 borrows the idea of a Haskell lens. A developer can look at the same graph
with different lenses to see different data. This implementation only covers the get
functionality: extracting the data and creating a new derived object.
Often the starting type of an RDF lens is (𝐺, 𝑥) where 𝐺 = (𝑉, 𝐸) ∧ 𝐸 ⊆
{{𝑠, 𝑝, 𝑜}|𝑠, 𝑝, 𝑜 ∈ 𝑉 } ∧ 𝑥 ∈ 𝑉 (called Cont in code). RDF Lens includes some ba-
sic lenses that can be combined into more complex ones. The predicate lens (pred),
for example, converts Cont into Array, matching all objects after following that
predicate, thus taking a step in the graph. Combining two of these predicate lenses and
combining the result, creates a lens that can extract a typed object Point with an X and
a Y coordinate (see Listing 2). The lenses are composable, Listing 2 shows that a lens
that extracts a Line can be composed from Point lenses.
1 const EX = "http://ex.org";
2 const xField = pred(`${EX}/field_x`).map(({id}) => ({x: id.value}));
3 const yField = pred(`${EX}/field_y`).map(({id}) => ({y: id.value}));
4 const pointLens = xField.and(yField).map(point => Object.assign(...point));
5
6 const startField = pred(`${EX}/start`).one().then(pointLens).map(start => ({start}));
7 const endField = pred(`${EX}/end`).one().then(pointLens).map(end => ({end}));
8 const lineLens = startField.and(endField).map(points => Object.assign(...points));
Listing 2: A lens that extracts a Point, with an X and a Y coordinate from an RDF
graph and a Lens that extracts a line from two points.
5
The implementation is available under the MIT license on github (https://github.com/ajuvercr/rdf-lens).
4. SHACL-based object extraction
To demonstrate the utility of RDF Lens, we implemented a lens that creates a lens from a
SHACL shape 6 . We extract each defined property and interpret that property according
to the sh:datatype or sh:class. If a sh:class is defined it will recursively apply a lens
that corresponds to that class. The resulting lens results in a dictionary, each field name
uses the value of sh:name. SHACL properties that define cardinality are also leveraged.
sh:minCount determines if a field is required or not, and sh:maxCount dictates whether
a single value or an array is to be expected. An example defining a Line can be seen at
Listing 3.
With RDF Lens is possible to combine the SHACL-derived lenses with custom lenses.
Listing 4 shows how to link a custom lens, with the undefined CBD class. In this case, the
lens extracts the Concise Bounded Description (CBD)7 set of triples, starting from the
current term. This lens is provided by the RDF Lens library but is possible to combine
externally defined lenses. Another predefined lens defined by RDF Lens, extracts a given
SHACL Path and results in a lens itself.
1 [] a sh:NodeShape;
2 sh:targetClass ;
3 sh:property
4 [ sh:name 'x'; sh:path ; sh:datatype xsd:float; sh:maxCount 1 ],
5 [ sh:name 'y'; sh:path ; sh:datatype xsd:float; sh:maxCount 1 ].
6
7 [] a sh:NodeShape;
8 sh:targetClass ;
9 sh:property
10 [ sh:name 'start'; sh:path ; sh:class ; sh:maxCount 1 ],
11 [ sh:name 'end'; sh:path ; sh:class ; sh:maxCount 1 ],
12 [ sh:name 'quads'; sh:path (); sh:class ; sh:maxCount 1 ].
Listing 3: A SHACL shape that defines a shape for a Line and a Point. This shape can
be interpreted by RDF Lens to extract a Line from RDF data.
5. Conclusion and future work
In this paper, we introduced RDF Lens as a developer tool to transform RDF triples into
a structure that can be used by an application. The tool is available as an open-source
library.
The idea that Haskell and RDF have a similar issue, resulted in the creation RDF lens
(for the JavaScript ecosystem). So far, we limited the scope for this proof of concept
to only extract objects from an RDF graph, and we do not allow yet for altering or
generating RDF from objects. This already resulted in a promising tool, and these
limitations will be addressed in future work.
6
The code is available in the repository: https://github.com/ajuvercr/rdf-lens/blob/master/src/shacl.ts
7
https://www.w3.org/submissions/CBD
1 import { NamedNode, Parser } from "n3";
2 import { CBDLens, extractShapes } from "rdf-lens";
3 const turtle = `
4 [ 5; 6; ];
5 [ 0; 7].
6 `;
7
8 const quads = new Parser().parse(turtle);
9 const shapes = extractShapes(shapeQuads);
10 shapes.lenses["CBD"] = CBDLens; // Externally combined lens
11
12 const lens = shapes.lenses["Line"]; // The lens that extracts a line
13 const point = lens.execute({id: new NamedNode("MyPoint"), quads});
14
15 // { "start": {"x": 5, "y": 6}, "end": {"x": 0, "y": 7}, "quads": [/* <6 quads> */] };
16 console.log(point);
Listing 4: Javascript code example combining SHACL-derived and custom lenses.
As RDF Lens is already used today in our tooling to facilitate dealing directly with
RDF data in JavaScript applications (both client- and server-side), we aim to support a
broader adoption of it in the RDF developer community. We hope this will be the start
of an ecosystem of reusable and composable RDF lenses.
References
[1] S. Staab, S. Scheglmann, M. Leinberger, T. Gottron, Programming the semantic web,
in: The Semantic Web: Trends and Challenges, Springer International Publishing,
Cham, 2014, pp. 1–5.
[2] J. Morgan, Linked data objects (ldo): A typescript-enabled rdf devtool, in: The
Semantic Web – ISWC 2023: 22nd International Semantic Web Conference, Athens,
Greece, November 6–10, 2023, Proceedings, Part II, Springer-Verlag, Berlin, Heidel-
berg, 2023, p. 230–246. doi:10.1007/978-3-031-47243-5_13.
[3] R. Verborgh, R. Taelman, LDflex: a read/write Linked Data abstraction for front-end
Web developers, in: Proceedings of the 19th International Semantic Web Conference,
volume 12507, 2020, pp. 193–211. doi:10.1007/978-3-030-62466-8_13.
[4] R. Lemmer, Haskell Design Patterns, Packt Publishing Ltd, 2015.
[5] P. Wadler, Comprehending monads, in: Proceedings of the 1990 ACM Conference
on LISP and Functional Programming, LFP ’90, 1990, p. 61–78. doi:10.1145/91556.
91592.
[6] S. L. Nita, M. Mihailescu, Lens, Apress, Berkeley, CA, 2019, pp. 145–151. doi:10.
1007/978-1-4842-4507-1_20.