Expression Pattern Language (eXPL)

eXPL Queries

A query is the launching point for eXPL execution and defines a goal to be achieved. The query along with the rest of the eXPL program is compiled and executed in the context of a Java application which must have access to the eXPL parser library and any resources on which the program depends. It is possible to report the query solution using a library, eg. output results on the system console, but normally the results will be extracted from either a result object returned from executing the query or a handler called at the end of every successful unification/evaluation cycle.

The next example contains an eXPL query name "customer_charge" which references 2 axiom sources, "charge" and "customer" and 2 templates, "freight" and "customer_freight":

axiom charge()
{"Athens", 23 ),
{"Sparta", 13 ),
{"Milos", 17);

axiom customer()
{"Marathon Marble", "Sparta"}
{"Acropolis Construction", "Athens"}
{"Agora Imports", "Sparta"}
{"Spiros Theodolites", "Milos"};

template freight(city, charge);
template customer_freight(
  name,
  city ? city == freight.city,
  charge
);

query customer_charge(charge:freight, customer:customer_freight);

This query can be run in the GreekConstruction application of the tutorial2. The console output when the query is run is:

freight(city = Athens, charge = 23)
customer_freight(name = Acropolis Cons., city = Athens, charge = 23)
freight(city = Sparta, charge = 13)
customer_freight(name = Marathon Marble, city = Sparta, charge = 13)
freight(city = Sparta, charge = 13)
customer_freight(name = Agora Imports, city = Sparta, charge = 13)
freight(city = Milos, charge = 17)
customer_freight(name = Spiros Theod., city = Milos, charge = 17)

The "customer_charge" query is declared thus:

customer_charge(charge:freight, customer:customer_freight)

It has 2 parameters, each specifying an axiom:template pair. The query proceeds in stages. At each stage the query unifies an axiom with a template. If a match occurs, the template is evaluated and the query proceeds to the next stage, otherwise, the cycle starts at the next iteration. The result of a successful unify/evaluate step is an axiom, named the same as the template, containing the results of the evaluation. This axiom is passed on to the next stage where it can be referenced using a 2-part name eg. "freight.city".

In stage 1, axiom "charge" is paired to template "freight" and in stage 2, axiom "customer" is paired to template "customer_freight". The query tries to combine all charge axioms with all customer axioms, but the solution contains only those cases where the name of the charge city corresponds to the customer city.

With cascading queries, a logical expression determines which axiom combinations are valid. Such expressions can incorporate terms from various sources. More than one expression used for complex logic and there is provision for real-world data where records may have missing entries.

Looking at the solution we see the Sparta freight axiom: freight(city = Sparta, charge = 13) appears twice because there are two customers in Sparta and each customer is encountered in a separate cycle. The only evaluation in the query is the logical expression "city ? city == freight.city". This translates to "if customer city matches frieight city, then continue". If the cities do not match, then the cycle commences at the next iteration.

Named Terms

A variation of customer_charge program is demonstrated in the NamedGreekConstruction application of the same tutorial. The axioms have named terms making the code easier to comprehend. The code is also more robust as the terms can be shuffled around without breaking the query. In fact, as a demonstration of position being unimportant with named terms, you will see that the charge template terms are reversed. Named terms will now be used for all the remaining examples.

axiom charge (city, charge)
{"Athens", 23 },
{"Sparta", 13 },
{"Milos", 17};

axiom customer (name, city)
{"Marathon Marble", "Sparta"},
{"Acropolis Construction", "Athens"},
{"Agora Imports", "Sparta"},
{"Spiros Theodolites", "Milos"};

// Terms reversed - so match by name
template freight(charge, city);
template customer_freight(
  name,
  city ? city == freight.city,
  charge = freight.charge
);

query customer_charge(charge:freight, customer:customer_freight);

Query Chaining

The initial solution can be further shaped by extending the tail of the query - hence the term "query chaining".