Pack your queries as Prolog predicates

One of the nice things about Prolog is that it can be used to name primitive queries. The named queries (predicates) can be combined with each other and other Prolog primitives to build more complex rules about the domain. Learning that is the goal of this lesson. The important take-home message is make your predicates small. We start with some examples.

Starting a program file

Run ?- emacs. in the toplevel to start the built-in editor. Use File/new... to create a new file, say demo.pl. Below is a skeleton for the file. This skeleton defines a Prolog module demo and imports the core RDF library and RDF-Schema libraries, which suffices for our purposes.

:- module(demo,
          [
          ]).
:- use_module(library(semweb/rdf_db)).
:- use_module(library(semweb/rdfs)).

Adding the first rule

A good rule to start with is event(X), which states that X is an event. Adding this rule to the template above results in this file below. Note that we exported event/1 from the module to make it available, we added the rule and a comment. Of course, the comment is optional, but it is used by ClioPatria to create an online manual for you.

:- module(demo,
          [ event/1		% ?Event
          ]).
:- use_module(library(semweb/rdf_db)).
:- use_module(library(semweb/rdfs)).

%%	event(?Event)
%
%	True if Event is an event in the Simple Event Model.

event(Event) :-
        rdfs_individual_of(Event, sem:'Event').

After saving the file, the file can be compiled from the toplevel or through the menu entry Compile/Compile buffer. Next, we can query the database. Use SPACE to get the next answer and RETURN to stop.

?- event(X).
X = poseidon:event_2005_001 ;
X = poseidon:event_2005_002 ;
...

Note that this rule represents a clean logical statement. With an unbound X, it enumerates all events while given a bounded X, it acts as a boolean test whether the given X is an event.

A more complicated example

In this example we wish to relate an event to a location on the globe. One of the complications here is that RDF literals always represent text (a string value), while latitude and longitude are normally expressed as floating point numbers. Please add the code below to the file demo.pl and add event_point/2 to the export-list.

%%	event_point(?Event, ?Point)
%
%	True if Event happened at point(Lat,Lon),   where  Lat and Lon
%	are the latitute and longitude of the event in decial degrees.

event_point(Event, point(Lat, Lon)) :-
        event(Event),
        rdf_has(Event, sem:hasPlace, Place),
        rdf_has(Place, wgs84:lat, literal(type(xsd:decimal, LatText))),
        rdf_has(Place, wgs84:long, literal(type(xsd:decimal, LonText))),
        atom_number(LatText, Lat),
        atom_number(LonText, Lon).

Tip: you can re-open a file or predicate in the editor using the command edit/1. Below are some examples. The system asks you to select the right item if multiple items match the query.

?- edit(demo).            % edit the file demo
?- edit(event_point).     % edit the predicate event_point

Prolog rules with resource arguments

The Prolog compiler transforms resource arguments of the form <prefix>:<local> to the global id form at compile time. When you write Prolog rules with resource arguments, you must identify the resource arguments arguments via the rdf_meta/1 declaration.

:- rdf_meta   my_rule(?, r).
my_rule(SomeArg, SomeResource) :-
        ...,
        rdf_has(SomeResource, sem:Place, Place),
        ...
        .

Exercises

In the two exercises below, you are asked to write two predicates. Add these predicates to the file demo.pl and add them to the export list of this module.

  1. Write a predicate event_in_box(Event, Lat1, Lon1, Lat2, Lon2) that finds all events inside a box bounded by a given latitude and longitude range. The Netherlands is roughly bounded by Lat 51..54 and Lon 2..8 degrees. Use your predicate to find all events in this box.
  2. Actor types (i.e., ship types) are organised in a hierarchy. Write a predicate event_actor_type(Event, ActorType) that finds all events in which a given type of actor (ship) is involved. Your predicate must also show events that are labeled with (transitive) subtypes of the given type. An example query is:
    ?- event_actor_type(Event, poseidon:atype_passenger_vessel).

You are now ready to Serve a web page.

See also
- Tutorial index