Semantic Web Tools
Prolog’s logic-based foundation makes it a natural fit for working with Semantic Web technologies — RDF, RDFS, OWL, and SPARQL. SWI-Prolog provides mature libraries for all of these.
Loading and Querying RDF Data
The Resource Description Framework (RDF) represents knowledge as a directed graph of subject-predicate-object triples. SWI-Prolog includes a highly optimized RDF database in its semweb library package.
The Turtle Format
While RDF can be serialized in XML (RDF/XML) or JSON (JSON-LD), the Turtle (Terse RDF Triple Language) format is the standard, human-readable serialization. In Turtle, triples are declared as space-separated terms ending with a period:
1 @prefix ex: <http://example.org/> .
2 ex:swi_prolog ex:implements ex:prolog .
You can group multiple statements about the same subject using a semicolon (;) or comma (,):
1 ex:prolog a ex:Language ;
2 rdfs:label "Prolog" ;
3 ex:paradigm ex:LogicProgramming .
Loading and Querying in Prolog
The library(semweb/rdf_db) module stores all loaded RDF triples in an internal database, which we query using rdf(?Subject, ?Predicate, ?Object). To parse Turtle files, we import library(semweb/turtle).

The rdf_explorer project wraps SWI-Prolog’s semweb library. Here is the complete file rdf_explorer/prolog/rdf_loader.pl:
1 %% rdf_loader.pl - Load and query RDF data using SWI-Prolog's semweb
2 %% library
3 :- module(rdf_loader, [
4 load_rdf_file/1,
5 query_rdf/3,
6 list_subjects/0,
7 describe_resource/1
8 ]).
9
10 :- use_module(library(semweb/rdf_db)).
11 :- use_module(library(semweb/rdfs)).
12 :- use_module(library(semweb/turtle)).
13
14 %% load_rdf_file(+FilePath) - Load RDF from Turtle or RDF/XML file
15 load_rdf_file(FilePath) :-
16 rdf_load(FilePath).
17
18 %% query_rdf(?S, ?P, ?O) - Query the RDF triplestore
19 query_rdf(S, P, O) :- rdf(S, P, O).
20
21 %% list_subjects - Print all unique subjects
22 list_subjects :-
23 setof(S, P^O^rdf(S, P, O), Subjects),
24 forall(member(S, Subjects), format(" ~w~n", [S])).
25
26 %% describe_resource(+URI) - Print all triples for a given subject
27 describe_resource(URI) :-
28 format("Describing: ~w~n", [URI]),
29 forall(
30 rdf(URI, P, O),
31 format(" ~w -> ~w~n", [P, O])
32 ).
Querying Remote SPARQL Endpoints
To query RDF data across the web, we use SPARQL (SPARQL Protocol and RDF Query Language). SWI-Prolog provides a standard SPARQL client in library(semweb/sparql_client). This module sends a SPARQL SELECT query over HTTP to a remote endpoint and parses the returned JSON or XML results into Prolog terms.
A SPARQL query typically specifies variables starting with a question mark (e.g. ?developer). The result of a query is returned as individual row(Value1, Value2, ...) terms containing the unified results for each row.

The sparql_client project provides convenient wrappers for sending queries. Here is the complete file sparql_client/prolog/sparql.pl:
1 %% sparql.pl - SPARQL client for querying remote endpoints
2 :- module(sparql, [
3 sparql_query_dbpedia/2,
4 sparql_query_wikidata/2,
5 sparql_query/3
6 ]).
7
8 :- use_module(library(semweb/sparql_client)).
9 :- use_module(library(http/http_client)).
10
11 %% sparql_query_dbpedia(+Query, -Results)
12 sparql_query_dbpedia(Query, Results) :-
13 sparql_query(Query, Results,
14 [host('dbpedia.org'), path('/sparql')]).
15
16 %% sparql_query_wikidata(+Query, -Results)
17 sparql_query_wikidata(Query, Results) :-
18 sparql_query(Query, Results,
19 [host('query.wikidata.org'), path('/sparql')]).
RDFS and OWL Reasoning
Standard RDF only represents direct relationships. To perform semantic reasoning, we use RDF Schema (RDFS) and the Web Ontology Language (OWL). RDFS introduces properties like:
rdfs:subClassOf: Declares hierarchical class inheritance.rdfs:subPropertyOf: Declares subproperty inheritance.rdfs:domain/rdfs:range: Restricts the types of subjects and objects a property can link.
Prolog is well-suited to reason over these rules, but SWI-Prolog’s library(semweb/rdfs) implements them directly in highly optimized C-level hooks, saving you from writing recursive rules yourself.
The most important predicates are:
rdfs_individual_of(?Resource, ?Class): Succeeds ifResourceis an instance ofClass, resolving any transitive class inheritance (viasubClassOf) and property domains/ranges.rdfs_subclass_of(?SubClass, ?SuperClass): True ifSubClassis a subclass ofSuperClass(either directly or transitively).rdfs_subproperty_of(?SubProperty, ?SuperProperty): True ifSubPropertyinherits fromSuperProperty.
For example, if we load an ontology stating that ex:LogicProgramming is a subclass of ex:ProgrammingParadigm, and ex:prolog has paradigm ex:LogicProgramming, the standard rdf/3 query won’t show that Prolog is a ProgrammingParadigm. However, RDFS reasoning unifies it immediately:
1 ?- rdfs_individual_of('http://example.org/prolog', 'http://example.org/ProgrammingParadigm').
2 true.
Practical Applications
You can combine local RDF data with remote SPARQL queries to build a domain-specific knowledge explorer. This hybrid architecture allows you to maintain private, local triples (such as proprietary client records or local system settings) while enriching them dynamically with global, public information from Wikidata or DBpedia.
Here is a Prolog module showing this pattern:
1 :- module(knowledge_explorer, [
2 explore_and_enrich/1
3 ]).
4
5 :- use_module(library(semweb/rdf_db)).
6 :- use_module(sparql_client/prolog/sparql).
7
8 %% explore_and_enrich(+ResourceURI)
9 %% 1. Find and print all local facts about the resource.
10 %% 2. Query DBpedia to fetch the English abstract/description.
11 explore_and_enrich(ResourceURI) :-
12 format("=== Local Knowledge for ~w ===~n", [ResourceURI]),
13 forall(
14 rdf(ResourceURI, P, O),
15 format(" Local: ~w -> ~w~n", [P, O])
16 ),
17
18 % Extract the local name from the URI to query DBpedia
19 % e.g., 'http://example.org/Prolog' -> "Prolog"
20 file_base_name(ResourceURI, LocalName),
21 format("~n=== Querying DBpedia for ~w ===~n", [LocalName]),
22
23 format(string(Query),
24 "SELECT ?abstract WHERE { \n\
25 <http://dbpedia.org/resource/~w> <http://dbpedia.org/ontology/abstract> ?abstract . \n\
26 FILTER (lang(?abstract) = 'en') \n\
27 } LIMIT 1", [LocalName]),
28
29 ( catch(sparql_query_dbpedia(Query, row(literal(Abstract))), _, fail)
30 -> format(" Abstract: ~w~n", [Abstract])
31 ; writeln(" Could not fetch remote abstract.")
32 ).
This pattern keeps your local codebase small and lightweight while placing the billions of triples of the Semantic Web at your logic engine’s disposal.