P. 1
Prova 3.0 User Guide - May 2010

Prova 3.0 User Guide - May 2010

|Views: 3|Likes:
Published by vermuz

More info:

Published by: vermuz on Aug 21, 2012
Copyright:Attribution Non-commercial

Availability:

Read on Scribd mobile: iPhone, iPad and Android.
download as PDF, TXT or read online from Scribd
See more
See less

02/26/2014

pdf

text

original

Sections

  • 2. Reactive messaging
  • 3. Event processing

Prova Rule Language Version 3.0
User’s Guide

Prova is an economic and efficient open source rule
language for creating distributed collaborating agents.
Running in Java runtime, it combines Prolog style logic
inference with extensions for Java scripting, concurrent
and scalable reactive messaging, workflows and event
processing suitable for Enterprise Service Bus deployment.

Alex Kozlenkov
May 2010

1. General language features .............................................................. 1

1.1.

Basic language elements ................................................................................................. 1
1.1.1. Simple (atomic) terms ................................................................................................. 2
1.1.2. Variables ...................................................................................................................... 2
1.1.3. Constants .................................................................................................................... 2
1.1.4. Compound terms (lists) ............................................................................................... 3
1.1.5. Facts ............................................................................................................................ 3
1.1.6. Rules ............................................................................................................................ 3

1.2.

Running Prova scripts from command line ..................................................................... 3

1.3.

Running Prova from Java ................................................................................................ 4
1.3.1. Passing Java objects to the initialization rulebase ...................................................... 5
1.3.2. Passing Java objects to the added rulebase fragment ................................................ 7
1.3.3. Submitting goals and obtaining solutions ................................................................... 7
1.3.4. Sending messages to the running rulebase ................................................................ 8
1.3.5. ProvaService—a simpler way to embed Prova agents ............................................... 8
1.3.6. Running Prova inside an OSGi container .................................................................. 11
1.3.7. Prova agent contract and ESB adaptor ..................................................................... 12

1.4.

Calling Java from Prova rulebases ................................................................................. 13
1.4.1. Using Java types and classes for Prova variables ...................................................... 13
1.4.2. Creating Java objects by calling their constructors ................................................... 15
1.4.3. Passing Prova lists as arguments to constructors ..................................................... 15
1.4.4. Invoking static methods ............................................................................................ 15
1.4.5. Accessing public static and instance fields ............................................................... 15
1.4.6. Using special built‐ins that take advantage of common Java classes ....................... 15

1.5.

Expressions .................................................................................................................... 15

1.6.

Global constants............................................................................................................ 16

1.7.

Prova maps for defining slotted terms ......................................................................... 16

1.8.

Metadata annotations .................................................................................................. 17

1.9.

Guards in Prova ............................................................................................................. 18
1.10. Workflows and business processes .............................................................................. 19
1.10.1. Simple gateways ................................................................................................... 19
1.10.2. Event‐driven gateways (edBPM) ........................................................................... 21
1.10.3. Predicate gateways ............................................................................................... 23
1.11. Functional programming extensions ............................................................................ 25
1.11.1. Representing functions ......................................................................................... 25
1.11.2. Functional composition ......................................................................................... 26
1.11.3. Lambda functions .................................................................................................. 26
1.11.4. Simple functional composition ............................................................................. 27

2

1.11.5. Monadic functional composition .......................................................................... 28
1.12. Builtins reference .......................................................................................................... 28

2. Reactive messaging ..................................................................... 47

2.1.

Design principles ........................................................................................................... 47

2.2.

Sending messages ......................................................................................................... 47
2.2.1. Position‐based arguments ........................................................................................ 47
2.2.2. Synchronized message sending with sendMsgSync ................................................. 50

2.3.

Using reaction rules for receiving messages ................................................................. 51
2.3.1. Global reaction rules ................................................................................................. 51
2.3.2. Inline reaction rules .................................................................................................. 52
2.3.3. Controlling the message reaction multiplicity .......................................................... 52
2.3.4. Explicit termination of inline reactions ..................................................................... 53

2.4.

Guards in message processing ...................................................................................... 54

2.5.

Concurrent reactive messaging .................................................................................... 54

2.6.

Annotations for message receiving ............................................................................... 58

2.7.

Reactive messaging for Java Swing ............................................................................... 59

3. Event processing ......................................................................... 60

3.1.

Event processing using reaction groups ....................................................................... 60
3.1.1. White box or workflow event processing ................................................................. 61

3.2.

Structure of reaction groups ......................................................................................... 61

3.3.

Annotations for reaction groups ................................................................................... 63

3.4.

Lifecycle of reaction group instances ............................................................................ 74

3.5.

Context variables with equality and WHERE constraints in AND groups ..................... 77

3.6.

Dynamic event channels ............................................................................................... 80

3.7.

EPTS Language Working Group ..................................................................................... 82
3.7.1. Flower delivery example ........................................................................................... 82

3.8.

Glossary ......................................................................................................................... 89

Prov

1. Gen

1.1.

Alth
Reader
given ad
The
simple s
new syn
refactor
increme
language
quality e
The

At th

va rule langu

neral lan

. Basic lang

hough the ro
will need to
dditional sem
syntactic si
syntax is easi
ntactic refin
ring. The ide
entally, with
es that foste
executable sy
following di

he top level,

uage 3.0

nguage fe

guage elem

ots of Prova
o keep an op
mantics and c
mplicity of t
ier to compr
nements eas
eal here is th
minimal cha
er quick exp
ystems.
agram captu

Prova offers

eatures

ents

lie in logic p
pen mind an
can do much
the language
rehend and m
sily available
hat of a deve
nges to the
perimentatio

ures the main

s three basic

User’s Gu

programming
nd accept th
h more than i
e is entirely
most importa
e via linear
eloper think
existing cod
on and yet,

n language e

c syntactic st

uide

g (LP), the si
hat syntactic
in a typical P
intentional
antly, easier
code chang
king about a
e. The langu
in the best

elements.

ructures for

milarity can
structures f
Prolog‐like la
and motivat
to scale up a
ges as oppo
new functio
uage is thus f
examples, li

putting toge

M

be deceptiv
familiar from
nguage.
ted by the i
and back do
osed to com
onality and
far closer to
ike Ruby, yie

ether a ruleb

May 2010

ve, so the
m LP, are

idea that
own, with
mplicated
adding it
scripting
eld high‐

base:

Prova rule language 3.0

User’s Guide

May 2010

2

rules,
facts (rules with no body),
goals (rules with no head).
These are built at the elemental level from
atomic terms,
compound terms (lists).
Let us talk about them in a bottom‐up fashion.

1.1.1. Simple (atomic) terms

Prova includes two types of simple terms: constants and variables. Internally, they are Java
interfaces ProvaConstant and ProvaVariable, respectively.

1.1.2. Variables

Prova variables are essentially hollow pointers that are yet to be given some value assignment.
Once the value is assigned, the variable becomes a constant term, that is a data wrapper around a
Java object. Once a constant is assigned, in a typical logic and functional programming way, it cannot
be assigned some other data. However, if a failure (inability to prove a new sub‐goal) is detected
during the goal evaluation, backtracking may result in alterantive branching, and therefore,
alternative assignments to variables. Assignment of values to variables ultimately is the main point
of running a Prova rulebase, i.e., providing answers to questions (goals) that are being asked.
As opposed to typical Prolog systems, variables can be typed (see the discussion in Calling Java
from Prova rulebases). Previous versions of Prova also allowed variables to be typed using OWL
ontologies. This feature is not available in Prova 3.0 but will return in the next version.
Syntactically, as common in Prolog, variables are represented by tokens starting with a capital
letter, in the case of typed variables, prefixed by fully qualified package and class name of the
corresponding Java class. It is also possible to use anonymous variables that begin with the
underscore character. Some examples of Prova variables follow.

A
Payload
Integer.I
com.betfair.prova.domain.Market.M
_

1.1.3. Constants

Constants may include numeric data using the Java syntax, strings in single or double quotes, as
well as fully qualified static or instance fields in Java objects. Single word strings that begin with
lower‐case letter are for all purposes the same as such strings in quotes.

12
300L
3.0e9
test
'test'
"test"

It is important to understand that during the execution of a Prova rulebase, variables get
assigned values essentially becoming constants. In this case, however, these variables are
syntactically represented as variables, yet for all purposes, behave like constants. For example, going
slightly beyond the scope of this sub‐section, this assignment sets the variable to a constant, and
subsequent re‐assignment of the same variable fails.

I=2,
I='cannot assign'

Special global constants have names starting with '$' as below.

$Count.addAndGet(Delta)

Prova rule language 3.0

User’s Guide

May 2010

3

1.1.4. Compound terms (lists)

Compound terms are in Prova represented as generic Prova lists. This lends itself well to agent
programming as any information can be easily distributed with agents sending each other Prova lists.
Critically, and uniquely different from typical LP languages, Prova keeps lists in arrays instead of
recursive lists.

Lists are represented in a standard Prolog way as

[Head|Rest]

Here Head is one or more (comma‐separated) elements that are the list's starting (prefix)
elements. The Rest is the list tail, that most often is just a variable that will match against the
remainder of the list. The vertical bar is then the same as the cons operator in functional languages.
Lists are an invaluable tool for pattern matching, so that we can say, we want those elements to be
there in the list and unification, which is the core operation in Prova, will match the lists and assign
values to free variables as appropriate.

1.1.5. Facts

A Prova fact is essentially a relation tuple (a record) with its elements being any term, be it
constant, variable, or a (possibly recursive) list. This is different from standard Prolog, where facts
normally cannot have variables. Moreover, facts can have embedded Java objects, or even global
constants. Facts have the following format.

predicate(arg1,...,argn).

It is often the case that there are more than one fact for a given predicate symbol.

alert_destination(no_bids,store2).
alert_destination(no_bids,store_manager2).

1.1.6. Rules

Prova rules typically are Horn rules that are first‐order disjunctions of literals with exactly one
positive literal. In logics, such Horn rules are known as definite clauses. The word typically highlights
the fact that in Prova, global reaction rules look exactly like Prova rules (with head literal for
predicate symbol rcvMsg) but their semantics are more aligned with reactive rules rather than
derivation rules.

Rules are written in the standard Prolog like way. Where ':‐' is pronounced as IF.

head_literal(args_h) :
body_literal1(args_1),
...,
body_literaln(args_n).

1.2. Running Prova scripts from command line

The new Prova main() is hosted by the ProvaCommunicatorImpl class. Currently, it requires the
following command‐line arguments:
1. agent name,
2. password (unused for now),
3. starting rulebase (with possibly included goals that are immediately run synchronously).
Optionally, the timeout for the agent could be specified after that with ‐t  in seconds>.
Specifying the option ‐t ‐1 allows the agent to run indefinitely, accepting goals as messages and
responding by other actions. If the timeout parameter is not provided, the engine executes all goals
in the starting rulebase and then if no internal messages were sent (as is the case with a typical
rulebase that has no message sending or receiving), it exits to the command shell. Otherwise, it
looks for one second long period of inactivity when no messages are detected and then exits to the
command shell.

The binary Prova distribution allows running the new Prova interpreter from command line using


prova3.bat [t ] arg0 arg1 ... argN

Prova rule language 3.0

User’s Guide

May 2010

4

The list of command‐line arguments is passed to the starting rulebase as global constants:

$0,$1..,$N.

Here is an example of the arguments passed to the runner:

agent password test001_args.prova anticoagulant

The following rulebase test001_args.prova then prints one solution Parent=molecular_function.

is_a("anticoagulant","molecular_function").

% Rules (how to derive new knowledge)
parent(X,Y) :% X is parent of Y IF Y is a (kind of) X
is_a(Y,X).
parent(X,Y) :% X is parent of Y IF X has a Y
has_a(X,Y).

% Goal (what to do/what to derive)
% Who is Parent of "anticoagulant"?
:solve(parent(Parent,$0)).

1.3. Running Prova from Java

Prova is written in Java but clearly, there needs to be a way for a generic Java program to
somehow 'initialise' a Prova agent and interact with it. The Prova agent that serves this purpose is
ProvaCommunicatorImpl, an implementation of the interface ProvaCommunicator. In the test
directory for the new Prova, test.ws.prova.test2, not all tests are created equal. Some work on
primitive constructs, explicitly creating variables or rules from scratch. We do not want to do that
here so let's create a ProvaCommunicator instance and give it a spin. When you create a
ProvaCommunicator, there is one main question you need to answer, what rulebase the agent will
initially run? Let's look at the test capture_enum() in test.ws.prova.test2.ProvaBuiltins1.java
reproduced below:

static final String kAgent = "prova";

static final String kPort = null;

@Test
public void capture_enum() {
final String rulebase = "rules/reloaded/test017.prova";
final int[] numSolutions = new int[] {2};
ProvaCommunicator prova = new
ProvaCommunicatorImpl(kAgent,kPort,rulebase,ProvaCommunicatorImpl.SYNC);
List solutions = prova.getInitializationSolutions();
org.junit.Assert.assertEquals(solutions.size(),1);
org.junit.Assert.assertEquals(solutions.get(0).length,numSolutions[0]);
org.junit.Assert.assertTrue(solutions.get(0)[0].getNv("Groups") instanceof ProvaList);
}

This is the simplest way to call Prova from Java. We create a ProvaCommunicatorImpl, passing it
(apart from the unique agent name and port, discussed elsewhere), the rulebase to be consulted
and indicating that the agent will be run in a synchronous mode. At the time of writing, this last
parameter is in fact, ignored. If the initial rulebase has any goals, they are run right in the
ProvaCommunicatorImpl constructor, making their results available when the client calls
ProvaCommunicator.getInititalizationSolutions(). This method returns a List, which
is a list of solution arrays, one each for each goal inside the initialisation rulebase. So in the current
example, we have one goal and two solutions for that goal. Each ProvaSolution contains name/value
pairs corresponding to variables' binding.The corresponding rulebase is shown below.

:solve(test017(Groups)).

Prova rule language 3.0

User’s Guide

May 2010

5

test017(Groups) :
Text="A doodle is a doddle",
% nondeterministically enumerate regular expression groups
capture_enum([Text,"(d?)(dl)"],Groups).

1.3.1. Passing Java objects to the initialization rulebase

Java objects can be passed to the Prova engine in a variety of ways. In particular, you can embed
Java objects directly into the initialization rulebase, i.e., the rulebase consulted when the
ProvaCommunicator is constructed, creating a Prova engine instance. The following code from
ProvaBuiltins1Test.java demonstrates.

@Test
public void read_enum() {
final String rulebase = "rules/reloaded/read_enum.prova";
final int[] numSolutions = new int[] {5};

Map globals = new HashMap();
globals.put("$File", rulebase);
ProvaCommunicator prova = new
ProvaCommunicatorImpl(kAgent,kPort,rulebase,ProvaCommunicatorImpl.SYNC,globals);
List solutions = prova.getInitializationSolutions();

org.junit.Assert.assertEquals(1,solutions.size());
org.junit.Assert.assertEquals(numSolutions[0],solutions.get(0).length);
}

This code passes a map globals with named constants (in this example, $File) to the consulted
rulebase read_enum.prova shown below.

:solve(test_read_enum_1(Line)).

test_read_enum_1(Line) :
fopen($File,Reader),
read_enum(Reader,Line).

The rulebase opens the file given the supplied filename. Any Java objects can be passed to the
rulebase in this way. For example, you can directly embed arbitrary Java objects into Prova facts like
this.

customer(1234,$c1).

Dealing with exceptions

If exceptions occur during the initialization, the engine wraps them in a RuntimeException with
the appropriate underlying exception reported as its cause. The following test shows a failure due to
non‐existence of the consulted rulebase.

@Test
public void initialization_from_nowhere() {
final String rulebase = "rules/reloaded/NOSUCHFILE.prova";

try {
comm = new ProvaCommunicatorImpl(kAgent,kPort,rulebase,ProvaCommunicatorImpl.SYNC);
} catch (Exception e) {
final String localizedMessage = e.getCause().getLocalizedMessage();
org.junit.Assert.assertEquals(
"Cannot read from rules/reloaded/NOSUCHFILE.prova",
localizedMessage);
}
}

Prova rule language 3.0

User’s Guide

May 2010

6

If there are parsing errors, the wrapped exception is a ProvaParsingException. This exception has
two fields that provide further information: src and errors, i.e, the file origin of the rulebase and a
collection of actual parsing errors. This collection maps lines and offsets in format line:offset to a
textual description of the encountered parsing error as reported by the parser. The following code
fragment illustrates.

@Test
public void initialization_with_parsing_errors() {
final String rulebase = "rules/reloaded/parsing_errors.prova";

try {
comm = new ProvaCommunicatorImpl(kAgent,kPort,rulebase,ProvaCommunicatorImpl.SYNC);
} catch (Exception e) {
org.junit.Assert.assertTrue( e.getCause() instanceof ProvaParsingException );
org.junit.Assert.assertEquals(
"rules/reloaded/parsing_errors.prova",
((ProvaParsingException) e.getCause()).getSource() );
org.junit.Assert.assertEquals( 5, ((ProvaParsingException) e.getCause()).errors().size());
}
}

Finally, if an exception occurs inside the rulebase, for example, caused by a failed method
invocation, the exception is also wrapped in a RuntimeException. The following rulebase
processing_errors.prova throws a NoSuchMethodException.

:solve(p(X)).

p(X) :
A=java.util.HashSet(),
A.nomethod().
The test below shows how to detect this exception.

@Test
public void initialization_with_rulebase_errors() {
final String rulebase = "rules/reloaded/processing_errors.prova";

try {
comm = new ProvaCommunicatorImpl(kAgent,kPort,rulebase,ProvaCommunicatorImpl.SYNC);
} catch (Exception e) {
final String localizedMessage = e.getCause().getLocalizedMessage();
org.junit.Assert.assertEquals(
"No such accessible method: nomethod() on object: java.util.HashSet",
localizedMessage);
}
}

Modifying the rules in a running engine

When a Prova engine instance is active, i.e, the ProvaCommunicator object is successfully
constructed, the latter can be used for adding and removing additional facts or rules to the running
rulebase. If the new consulted code contains goals, these goals will then be executed.
The example below shows the use of the ProvaCommunicator.consultSync method to add a new
fragment to the running rulebase.

String inputRules = "dissemination(Person,public_reports) :clearance(Person,low).";
BufferedReader inRules = new BufferedReader(new StringReader(inputRules));

try {

Prova rule language 3.0

User’s Guide

May 2010

7

comm.consultSync(inRules, "disseminationrules", new Object[]{});
} catch (Exception e) {
// TODO: process exceptions
}

The method takes three parameters:
1. either a String or BufferedReader with a rulebase fragment in Prova;
2. a String with the logical name given to the rulebase fragment;
3. an array of Java objects to replace any placeholders embedded in the fragment.
The call may raise exceptions that the user is required to process.

Removing or replacing the code

The name of the rulebase fragment can be used later for removing the previously added

fragment.

// Remove all clauses consulted for this key
comm.unconsultSync("disseminationrules");

The fragment can be then easily updated by consulting it again with a new version in a separate

step.

1.3.2. Passing Java objects to the added rulebase fragment

The source code submitted to consultSync can contain placeholders in the syntax _0,...,_n that
the call will replace with the Java objects supplied in the third parameter in the rulebase.

String input = "robot(_0).";
BufferedReader in = new BufferedReader(new StringReader(input));

try {
comm.consultSync(inRules, "robotrules", new Object[]{new Robot("Dave")});
} catch (Exception e) {
// TODO: process exceptions
}

This is actually more powerful than what you can do from a rulebase consulted from an external
file during initialization because to embed a Java object in a fact would have required explicitly
constructing a Java object and adding a fact dynamically using assert.

Robot=com.independentrobots.Robot(),
assert(robot(Robot))

1.3.3. Submitting goals and obtaining solutions

If the fragment rulebase contains clauses for meta‐predicates eval or solve, they are executed as
new goals. The solutions resulting from the solve goals (eval do not produce any solutions) are
returned in a List in the same way it works for initialization solutions.

String input = ":solve(dissemination(Person,public_reports)).";
BufferedReader in = new BufferedReader(new StringReader(input));

try {
List resultSets = comm.consultSync(in, Integer.toString(key++), new Object[]{});
for( ProvaSolution[] resultSet : resultSets ) {
org.junit.Assert.assertEquals(numSolutions[i++],resultSet.length);
}
} catch (Exception e) {
// TODO: Process exceptions
}

Prova rule language 3.0

User’s Guide

May 2010

8

1.3.4. Sending messages to the running rulebase

There are two ways to send a message to the running Prova rulebase from Java. The low‐level
ProvaCommunicator.addMsg method can be used for sending a message constructed from a Prova
term explicitly built from the basic building primitives, such as constants or lists. The example below
demonstrates.

// Send a hundred messages to the consulted Prova rulebase.
// Processing is done concurrently on threads belonging to the async pool.
for( int i=0; i<100; i++ ) {
ProvaList terms = ProvaListImpl.create( new ProvaObject[] {
ProvaConstantImpl.create("test"+i),
ProvaConstantImpl.create("async"),
ProvaConstantImpl.create(0),
ProvaConstantImpl.create("inform"),
ProvaListImpl.create(new ProvaObject[] {
ProvaConstantImpl.create("a"),
ProvaConstantImpl.create(i)
})
});
comm.addMsg(terms);
}

The message needs to be compliant with the standard Prova message format described in

Sending messages.

Another method ProvaCommunicator.sendMsg offers a more direct, but slower, way to send

messages:

sendMsg(String xid, String protocol, Object receiver, String perf, String payload, Object[] objs)

This signature directly corresponds to the standard message format, see Sending messages.

1.3.5. ProvaService—a simpler way to embed Prova agents

This usage idiom is based on a new feature, a ProvaService interface and default implementation
ProvaServiceImpl, designed for both plain Java runtime and OSGi containers.
The host Java code creates and initializes a ProvaService, which can hold one or more Prova
agents each running in their own Prova engine. In fact, Prova supports two ways of modularization in
an OSGi environment: mapping one agent to a separate bundle or running multiple agents in one
bundle. In a plain standalone non‐OSGi Java process, the latter is the only option, and this Section
shows how simple it is to embed multiple agents and have them communicate via messaging
between themselves and with the embedding Java code‐‐also considered to be an "embedding
agent".

We reproduce below the source code for a standalone runner class ProvaSimpleService.java that
demonstrates how to use ProvaService to create two cooperating agents. An alternative way to
achieve that would have been to use a Mule ESB implementation although the example presented
below has a net advantage in terms of simplicity and lack of massive numbers of dependencies
required by Mule.

package ws.prova.examples.runner;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

import ws.prova.service.EPService;
import ws.prova.service.ProvaService;
import ws.prova.service.impl.ProvaServiceImpl;

/**
* Demonstrate how ProvaService can host multiple Prova agents that communicate with each other
* using the "osgi" protocol.

Prova rule language 3.0

User’s Guide

May 2010

9

* Note that this runs in plain Java‐‐no OSGi container is actually required.
*
* The runner implements EPService for Prova engines to be able to send messages to the runner,
* not just between themselves.
*
* @author Alex Kozlenkov
*/
public class ProvaSimpleService implements EPService {
static final String kAgent = "prova";
static final String kPort = null;
final String sender_rulebase = "rules/service/message_passing/sender.prova";
final String receiver_rulebase = "rules/service/message_passing/receiver.prova";
private ProvaService service;

public ProvaSimpleService() {
service = new ProvaServiceImpl();
service.init();
}

private void run() {
// Create two Prova agents in their own engines
String sender = service.instance("sender", "");
String receiver = service.instance("receiver", "");

// Consult one rulebase into each
service.consult(receiver, receiver_rulebase, "receiver1");
// Make sure the consumer has access to a global object for counting
AtomicInteger count = new AtomicInteger();
service.setGlobalConstant(receiver, "$Count", count);
service.consult(sender, sender_rulebase, "sender1");

// Send a message directly from this runner
Map Integer> payload = new HashMap Integer>();
payload.put("a", 2);
// This runner names itself as "runner" agent in this call (see the third argument).
// This EPService implementation is passed in the last argument.
// The service will then register an EPService callback so that agents will be able to
// send messages to this runner setting the destination to "runner".
service.send("xid", "receiver", "runner", "inform", payload, this);

try {
synchronized(this) {
wait(2000);
// Verify that the count had been incremented
assert(count.get()==2);
}
} catch (Exception e) {
}
System.out.println("Confirmed that the messages have been received");

// Bring both engines down: if we had not done that, both agents would have continued

indefinitely

service.destroy();
}

/**
* This is a callback that is called when a Prova engine (see receiver.prova) sends a message to this

runner

*/

Prova rule language 3.0

User’s Guide

May 2010

10

@Override
public void send(String xid, String dest, String agent, String verb,
Object payload, EPService callback) {
System.out.println("Received "+verb+" from "+agent+" :"+payload);
}

public static void main( String[] args ) {
new ProvaSimpleService().run();
}
}
This runner code uses two rulebases below. This is a "receiver" agent in rules/service/receiver.prova.
:eval(receiver()).

receiver() :
rcvMult(XID,Protocol,From,inform,{a>I}),
println(["Received ",{a>I}]),
$Count.incrementAndGet(),

% This only makes sense if run from ProvaSimpleService.java
sendMsg(XID,osgi,runner,inform,{a>I}).
This is a "sender" agent in rules/service/sender.prova.
:eval(sender()).

sender() :
sendMsg(XID,osgi,receiver,inform,{a>1}).

When the runner code is initialized in the ProvaSimpleService constructor, it creates and
initializes a ProvaService instance. It then creates two Prova agents using the ProvaService.instance()
method and then uses the method consult to add the Prova rulebases to those agents. When the
rulebases are consulted, they run their eval goals with the sender sending and the receiver receiving
a message. For the agents to be "visible" to each other, they must communicate via the "osgi"
protocol (the second argument in sendMsg and rcvMsg).
Using a global object count is only needed in this slightly contrived example: we use it for
incrementing the count of expected messages (see $Count.incrementAndGet() in receiver.prova) and
checking this count from the Java code.
The example also demonstrates that we can both send messages to the selected agents and
receive messages back from the Java code. For this to work, the key is to pass a chosen name of the
containing "agent" to the _ProvaService.send()_ method:
service.send("xid", "receiver", "runner", "inform", payload, this);
ProvaService registers the name "runner" against the callback object passed as the last
parameter. Note that the ProvaSimpleService implements the EPService interface, allowing it the
Prova agents to call back on the EPService.send() method when the need to send messages back to
the containing agent. Observe the sendMsg with destination "runner" in receiver.prova.
sendMsg(XID,osgi,runner,inform,{a‐>I}).
Also, observe the use of Java Map for passing message payloads between all parties, including
the embedding agent. This improves performance and yet allows for considerable flexibility (see
Prova maps and messaging using slotted terms).
This is a typical output from running this program.

Prova Service 7faee1dea6e1421dac2e97709ba47196 created
==========Service messaging test: receiver==========
==========Service messaging test: sender==========
Received {a=1}
Received {a=2}
Received inform from receiver :{a=1}
Received inform from receiver :{a=2}
Confirmed that the messages have been received
Prova Service 7faee1dea6e1421dac2e97709ba47196 destroyed

Prova rule language 3.0

User’s Guide

May 2010

11

To summarize, we are able to exchange messages between all parties: the Java code
representing an embedding agent and the embedded Prova agents. The messages can flow in any
direction between all parties with only logical agent names required as destination.

1.3.6. Running Prova inside an OSGi container

Prova 3.0 is packaged as an OSGi bundle that can expose a Prova service and necessary domain
classes accessible from other bundles in an OSGi container. We use META‐INF/MANIFEST.MF
reproduced below for declaring the bundle dependencies and declaring packages and services useful
for other bundles.

ManifestVersion: 1.0
BundleVersion: 3.0.0
BundleName: Prova compact
BundleManifestVersion: 2
BundleDescription: Prova rule engine compact
BundleSymbolicName: ws.prova.compact
BundleRequiredExecutionEnvironment: J2SE1.5
ImportPackage: org.osgi.framework;version="1.3.0",
org.apache.commons.collections;version="3.2.1",
org.apache.commons.beanutils;version="1.8.0",
org.apache.log4j;version="1.2.15"
RequireBundle: com.springsource.org.antlr.runtime;bundleversion="3.1.3"
ExportPackage: ws.prova.service;version="3.0.0",
ws.prova.api2;version="3.0.0",
ws.prova.exchange;version="3.0.0",
ws.prova.exchange.impl;version="3.0.0"
BundleClassPath: .,
target/classes/,
rules
BundleActivationPolicy: lazy

As we can see, the number of dependencies that Prova relies on is very small. We also use Spring
DM context configurations in META‐INF/spring to make ProvaService available to other bundles as a
service. When an OSGi target platform starts, it exposes the ProvaService as an OSGi service without
us having to have any dependency or coupling in the project itself on OSGi or Spring.
You can download the platform from: http://www.prova.ws/downloads/platform.zip. Unzip it to
a directory and from Eclipse go to Window/Preferences/Plugin development/Target platform and
create and choose a new target platform using the directory you expanded the platform bundles
into.

Check

out

the

Prova

Eclpse

project

from
https://mandarax.svn.sourceforge.net/svnroot/mandarax/prova3/prova‐compact/trunk and run
mvn clean install ‐DskipTests from command line. Refresh the project in Eclipse.
You can run the Prova bundle in an OSGi container by first creating a new run configuration by
going to Run/Run configurations.../OSGi Framework including the Prova bundle from the Prova
project in addition to all the bundles included in the imported platform. Running this configuration
will output something like this to the console:

osgi> 0 [Start Level Event Dispatcher] INFO
org.springframework.osgi.extender.internal.activator.ContextLoaderListenerStarting
[org.springframework.osgi.extender] bundle v.[1.2.1]
90 [Start Level Event Dispatcher] INFO
org.springframework.osgi.extender.internal.support.ExtenderConfigurationNo custom extender
configuration detected; using defaults...
99 [Start Level Event Dispatcher] INFO org.springframework.scheduling.timer.TimerTaskExecutor

Initializing Timer
185 [Start Level Event Dispatcher] INFO
org.springframework.osgi.extender.support.DefaultOsgiApplicationContextCreatorDiscovered
configurations {osgibundle:/METAINF/spring/*.xml} in bundle [Prova compact (ws.prova.compact)]

Prova rule language 3.0

User’s Guide

May 2010

12

357 [SpringOsgiExtenderThread1] INFO
org.springframework.osgi.context.support.OsgiBundleXmlApplicationContextRefreshing
OsgiBundleXmlApplicationContext(bundle=ws.prova.compact, config=osgibundle:/METAINF/spring/*.xml):
startup date [Thu Apr 29 13:54:59 BST 2010]; root of context hierarchy
358 [SpringOsgiExtenderThread1] INFO
org.springframework.osgi.context.support.OsgiBundleXmlApplicationContextApplication Context service
already unpublished
426 [SpringOsgiExtenderThread1] INFO
org.springframework.beans.factory.xml.XmlBeanDefinitionReaderLoading XML bean definitions from URL
[bundleentry://59.fwk1384828782/METAINF/spring/modulecontext.xml]
755 [SpringOsgiExtenderThread1] INFO
org.springframework.beans.factory.xml.XmlBeanDefinitionReaderLoading XML bean definitions from URL
[bundleentry://59.fwk1384828782/METAINF/spring/moduleosgicontext.xml]
907 [SpringOsgiExtenderThread1] INFO
org.springframework.osgi.extender.internal.dependencies.startup.DependencyWaiterApplicationContextExe
cutor
No outstanding OSGi service dependencies, completing initialization for
OsgiBundleXmlApplicationContext(bundle=ws.prova.compact, config=osgibundle:/METAINF/spring/*.xml)
911 [SpringOsgiExtenderThread2] INFO
org.springframework.beans.factory.support.DefaultListableBeanFactoryPreinstantiating singletons in
org.springframework.beans.factory.support.DefaultListableBeanFactory@57c8b24d: defining beans
[provaService,org.springframework.osgi.service.exporter.support.OsgiServiceFactoryBean#0]; root of
factory hierarchy

Prova Service 090128f4d886400aa63185088bdd08e0 created
996 [SpringOsgiExtenderThread2] INFO
org.springframework.osgi.service.exporter.support.OsgiServiceFactoryBeanPublishing service under
classes [{ws.prova.service.ProvaService}]
999 [SpringOsgiExtenderThread2] INFO
org.springframework.osgi.context.support.OsgiBundleXmlApplicationContextPublishing application
context as OSGi service with properties {org.springframework.context.service.name=ws.prova.compact,
BundleSymbolicName=ws.prova.compact, BundleVersion=3.0.0}
1006 [SpringOsgiExtenderThread2] INFO
org.springframework.osgi.extender.internal.activator.ContextLoaderListenerApplication context
successfully refreshed (OsgiBundleXmlApplicationContext(bundle=ws.prova.compact,
config=osgibundle:/METAINF/spring/*.xml))

This confirms that the Prova bundle started successfully.

1.3.7. Prova agent contract and ESB adaptor

The way to start a Prova engine from Java described in the previous Section does not follow any
particular pattern. In particular, there is no way for the Prova rulebase to take an active role and
communicate with the embedding Java code, apart from passing a callback object to the rulebase
and the latter invoking the callback when the rulebase needs to send data.
Prova 3.0 includes the following simple ProvaAgent interface that imposes a contract on the
adaptor Java object that embeds a Prova engine instance. In a separate mule‐prova‐agents project,
we use a Mule 2.1 Enterprise Service Bus implementation of this contract. This allows Prova agents
to be provisioned by the Mule ESB configuration and communicate with each other via a variaty of
transports supported by Mule. If another ESB (or equivalent) implementation is required, all that is
required is implementing the ProvaAgent interface described below.

public interface ProvaAgent {
public void send(String dest, ProvaList terms) throws Exception;
public String getAgentName();
}

We recommend consulting the Prova3AgentImpl.java code for an implementation suitable for
Mule ESB as it provides a simple example of the required functionality. As described in the Sending
messages (in particular, see the discussion on Protocol), the agent rulebase sending messages using

Prova rule language 3.0

User’s Guide

May 2010

13

the "esb" protocol will automatically hit the method ProvaAgent.send, which then will publish them
into the ESB layer to the indicated destination.

@Override
public void send(String dest, ProvaList terms) throws Exception {
MuleClient client = new MuleClient();
client.dispatch(dest,terms,null);
}

Conversely, all messages received by the agent will indicate the received protocol as "esb" but
typically will override that with the "async" protocol to allow for the same conversation‐id to be
mapped to the same agent thread.

public Object onCall(MuleEventContext context) throws Exception {
MuleMessage inbound = context.getMessage();
ProvaList terms = null;
if( inbound.getPayload() instanceof ObjectMessage ) {
terms = (ProvaList) ((ObjectMessage) inbound.getPayload()).getObject();
} else {
terms = (ProvaList) context.getMessage().getPayload();
}

// Add the message as a goal to the asynchronous Prova Communicator queue
if( !"".equals(targetProtocol) ) {
terms.getFixed()[1] = new ProvaConstantImpl(targetProtocol);
}
comm.addMsg(terms);

// We are done, everything is asynchronous
context.setStopFurtherProcessing(true);

return null;
}

1.4. Calling Java from Prova rulebases

Prova agents run in Java so it is natural that Prova rulebases can also call home and work with
Java data and objects. This integration includes:
Using Java types and classes for Prova variables;
Creating Java objects by calling their constructors;
Passing Prova lists as arguments to Java constructors;
Invoking static methods, including object creation by using factory methods;
Accessing public static and instance fields;
Using special built‐ins that take advantage of common Java classes;
Adding new Prova built‐ins in Java.

1.4.1. Using Java types and classes for Prova variables

According to some Prolog experts and implementers, the ansence of variables typing in Prolog is
one of the reasons of its relative failure to become completely mainstream. Prova takes a more
practical approach by allowing Java types explicitly and clearly annotating variables using a simple
syntax as in the following example ra001.prova:

:solve(a(3)).
:solve(a(Number.X)).
:solve(a(Integer.I)).
:solve(a(Short.I)).

a(Integer.I).

Prova rule language 3.0

User’s Guide

May 2010

14

a(Double.I).

% ==================
% This prints:
% yes
% X=java.lang.Integer.<7>
% X=java.lang.Double.<10>
% I=java.lang.Integer.<13>
% no

Unification that includes variable types follows the following subsumption rules.
1. If the query (goal) is 'asking' for a narrower type, it will not match a wider type in the target

literal.
2. A wider query will match a more specialized target literal.
Observe how uninstantiated variables also benefit from this with the second goal
solve(a(Number.X)) matching any applicable rules that are more specialized.
Note that classes in the java.lang package do not require to be full qualified. Fully qualified class
names can also be used in typed variables. The following code shows how the builtins retract,
retractall, and insert take into account typed variables.

:solve(ra002_1(X,Y)).
:solve(ra002_2(X,Y)).
:solve(ra002_3(X,Y)).

f(ws.prova.examples.IBMPortfolio.N,Integer.M).
f(ws.prova.examples.Portfolio.N,M).
f(ws.prova.examples.IBMPortfolio.N,Integer.M).
f(2,3).

ra002_1(X,Y) :
println(["‐‐‐‐‐"]),
% The first fact not more general than this is removed by retract below
retract(f(ws.prova.examples.Portfolio.N,M)),
f(X,Y).
ra002_2(X,Y) :
println(["‐‐‐‐‐"]),
% Facts more general than this are not removed by retractall below
retractall(f(ws.prova.examples.IBMPortfolio.N,Integer.M)),
f(X,Y).
ra002_3(X,Y) :
println(["‐‐‐‐‐"]),
insert(f(ws.prova.examples.Portfolio.N,Integer.M)),
% Does not add anything as it subsumes a fact already in the KB
insert(f(ws.prova.examples.Portfolio.N,M)),
f(X,Y).

% ==================
% This prints:
% ‐‐‐‐‐
% X=ws.prova.examples.Portfolio.<30>, Y=<27>
% X=ws.prova.examples.IBMPortfolio.<39>, Y=java.lang.Integer.<40>
% X=2, Y=3
% ‐‐‐‐‐
% X=ws.prova.examples.Portfolio.<80>, Y=<77>
% X=2, Y=3
% ‐‐‐‐‐
% X=2, Y=3
% X=ws.prova.examples.Portfolio.<137>, Y=java.lang.Integer.<138>
% X=ws.prova.examples.Portfolio.<150>, Y=<146>

Prova rule language 3.0

User’s Guide

May 2010

15

1.4.2. Creating Java objects by calling their constructors

Prova allows Java constructors to be invoked using the familiat Java syntax, only dropping the
new keyword. Here are a few examples.

L=java.util.ArrayList()
DF=java.text.SimpleDateFormat("dd/MM/yyyy kk:mm:ss.mmm")
Calendar=java.util.GregorianCalendar()

1.4.3. Passing Prova lists as arguments to constructors

When a Prova list is passed as an argument to a Java constructor, we follow the agreement that
the top‐level elements in the supplied list are unwrapped if necessary from the Prova constant
representation to naked Java objects and the list itself is then shipped to the method (or
constructor) as Java list containing these objects. In the case of ordinary Java method calls, Prova
lists
 are passed to the list unchanged.

This fragment from test004.prova illustrates.
List=java.util.ArrayList([1,"2"])

1.4.4. Invoking static methods

Here is a typical example of a static method invocation, in this instance, this is a factory method.

Model = com.hp.hpl.jena.rdf.model.ModelFactory.createDefaultModel()

1.4.5. Accessing public static and instance fields

Public static fields can be accessed using the familiar Java syntax. This example uses a few public
enumerations as well as the standard out stream as a parameter in a method.

Child1 = Model.createResource(),
Child1.addProperty(com.hp.hpl.jena.vocabulary.VCARD.Given,GivenName),
Child1.addProperty(com.hp.hpl.jena.vocabulary.VCARD.Family,FamilyName),
JohnSmith.addProperty(com.hp.hpl.jena.vocabulary.VCARD.N,Child1),
Model.write(System.out),

1.4.6. Using special builtins that take advantage of common Java classes

At the moment, there are two built‐in predicates that use Java data matching appropriate

interfaces.

The element built‐in can non‐deterministically extract elements from instantiated Java iterators.

Iter = Model.listStatements(),
element(Stmt,Iter),

The findall built‐in accumulates the resultset of a goal in a Java ArrayList. For example, the
following rule accumulates all the solutions of the goal [X|Xs] in the new list L, which is then sent
back to the caller as a whole in one reply message.

% Reaction rule to a general queryref_acc
rcvMsg(XID,Protocol,From,queryref_acc,[ID,[X|Xs]]) :
findall([X|Xs],[X|Xs],L),
sendMsg(XID,Protocol,From,reply,[ID,L]).

1.5. Expressions

Prova 3.0 can handle arithmetic expressions on the right‐hand side of binary operators. The

grammar is shown below.

expr : aterm ((PLUS | MINUS) expr)?;

aterm : (MINUS? variable | number | MINUS? predicate_java_call | OPEN expr CLOSE) (( MULT | DIV |

REM ) aterm)?;

predicate_java_call
: static_java_call | instance_java_call

Prova rule language 3.0

User’s Guide

May 2010

16

;

It is currently impossible to use expressions in arguments of Java calls. It is also not allowed to
use Java constructors in expressions unless in isolation. Here are some examples.

expr001(N,M) :
N=M+1*2.
expr001(N,M) :
N<=M+1*2.
expr001(N,M) :
N>(M+1)*2.

expr002(N,M) :
N=java.lang.Math.min(1,M)+1.
expr002(N,M) :
L=java.util.ArrayList(),
L.add(2),
N=L.get(0)+L.size().

1.6. Global constants

Global constants is a new functionality in Prova. Look at the following example

globals001.prova.

:solve(globals(N)).

globals(N) :
data($A,N).
globals(N) :
$A=b,
data($A,N).

data(a,1).
data(b,2).

Any constant with name starting with the $ sign is a global constant. These constants are part of
the _ProvaKnowledgebaseImpl_ instance and can be instantiated prior to parsing a new rulebase or
modified (or added) during the execution of the code. This is accomplished by using the syntax
$Var=.

The code above is run from the new test ProvaGlobalsTest.globals001() that initially sets $A to
'a' so it returns two solutions: 1 and 2.
The intended use of global constants $NNN is for passing command‐line arguments to Prova
scripts. This functionality will be added once the main() runner is added to the new Prova code.

1.7. Prova maps for defining slotted terms

Slotted terms is a highly valuable feature of languages like F‐Logic, standards (RIF or RuleML),
and products (like OO‐jDREW). They also make interoperability with forward‐chaining rule systems
like JBoss Rules far easier.

Prova 3.0 includes an implementation of slotted terms using the W3C RIF arrow expression
syntax as in the following example map2.prova. Note that Prova also allows to use colon ':' in
key:value instead of key‐>value. The keys are currently limited to strings.

:solve(own(X)).

buy({buyer>'Bill',seller>'Amazon',item>'Avatar Bluray'}).

keep({keeper>'Bill',item>'Avatar Bluray'}).

own({owner>Person,item>Object}) :

Prova rule language 3.0

User’s Guide

May 2010

17

buy({buyer>Person,seller>Merchant,item>Object}),
keep({keeper>Person,item>Object}).

This shows that Prova maps can be used as sole arguments of predicates, essentially replacing
position‐based syntax with one based on named arguments. The example prints:

X={item=Avatar Bluray, owner=Bill}

The test map2() in ProvaBuiltins1Test.java is based on this example.
Slotted terms are especially useful in reactive messaging, see the discussion on slotted terms in

reactive messaging.

1.8. Metadata annotations

The old Prova version had an interesting but somewhat patchy approach to run‐time processing
of the rule metadata. Prova 3.0 is aiming to improve on this. The first question is what to annotate
with metadata? It is clear that all Prova clauses (rules and facts) should be allowed to carry
metadata. As you'll see below, body literals can also be annotated so that whenever unification
occurs, it uses metadata matching if metadata is present on the body literal.
The examples below are taken from a new test rules/reloaded/label.prova. This is the way

metadata is added to rules:

@label(rule1) r1(X):q(X).
@label(rule2) r2(X):q(X).
@label(rule3) r2(X):q(X).

Metadata is a set of annotations each of which is a pair defined as: @key(value [,value]*). Both
keys and values are arbitrary strings defined by their occurrence in an annotation. All rules in a
rulebase consulted from a file automatically have a special annotation @src(FILENAME). Apart from
that, all other rule annotations are defined by the user. There could be more than one user‐defined
annotation associated with a single rule. There is no requirement for annotations to be on the same
line as the rule head.

Now there wouldn't be any real use for this if we couldn't somehow take this metadata into
account when the rulebase is executed. To achieve that, the body literals in a rule can be optionally
annotated as well. Here is an example that returns 3 solutions (1,2,3):

p1(X):
@label(rule1)
r1(X).
q(1).
q(2).
q(3).

:solve(p1(X2)).

The annotations on literals (as opposed to rules) are a set of constraints on the target rules
during unification. For each literal annotation, there must be at least one match between a value
listed for that annotation and a value listed for the same key in the target rule. This is a more
complex example that also has three solutions (1,2,3):

% succeeds since scope is EITHER "rule2" OR "rule1"
p2a(X):
@label(rule2,rule1)
r1(X).
:solve(p2a(X4)).

Imagine now that we do not know what are the target annotation values and would like to
discover and possibly trace the annotations for target rules matching a literal. How about trying to
use a variable instead of a constant value in a literal annotation? The following returns 6 solutions
where Label6 takes values 'rule2' and 'rule3' 3 times each.

p4(X,Y):
@label(Y)
r2(X).

Prova rule language 3.0

User’s Guide

May 2010

18

:solve(p4(X6,Label6)).

Observe that if a match is found, execution continues for r2(X) as though the @label annotation

was not present.

Now since we have variables in literal annotations, we can also pre‐assign values to these
variables dynamically. The following code sets the annotation value at run‐time to 'rule2' so that
only 3 solutions for X7 are returned (1,2,3).

% dynamically set the metadata value expected from matching rules
:solve(p4(X7,rule2)).

Finally, we can also find the rulebase of the target clause in a unification. The final example
shows how we can constrain one annotation (@label) while enquiring on the value of another
(@src). In this way, any match can be traced from the source rulebase of the target clause. This
returns 3 solutions with Src9 set to the filename of the rulebase in each solution.

% get module label
p6(X,Y):
@src(Y) @label(rule3)
r2(X).
:solve(p6(X9,Src9)).

1.9. Guards in Prova

Prova 3.0 implements a new inference extension called literal guards. Let's start with an
example. Imagine that during unification, the target rule matches the source literal but we do not
want to proceed with further evaluation unless a guard condition evaluates to true. The guards are
specified in a syntax that is similar to the one used in Erlang (using brackets instead of the when
keyword) but the semantics are more appropriate for a rule language like Prova. See
rules/reloaded/guard.prova and ProvaMetadataTest.java.


@author(dev1) r1(X):q(X).
@author(dev22) r2(X):q(X).
@author(dev32) r2(X):s(X).

q(2).
s(2).

trusted(dev1).
trusted(dev22).
% Author dev22 is trusted but dev32 is not, so one solution is found: X1=2
p1(X):
@author(A)
r2(X) [trusted(A)].
:solve(p1(X1)).

This example uses metadata annotations on rules for predicates r1/1 and r2/1 and on the literal
r2(X) in the body of the rule for p1(X) (see Metadata annotations). Since variable A in @author(A) is
initially free, it gets instantiated from the matching target rule(s). Once A is instantiated to the target
rule's @author annotation's value ('dev22', for the first r2 rule), the body of the target rule is
dynamically non‐destructively modified to include all the literals in the guard trusted(A) before the
body start, after which the processing continues. Since trusted(dev22) is true but trusted(dev32) is
not, only the first rule for predicate r2 is used and so one solution X1=2 is returned by solve(p1(X1)).
Guards can include arbitrary lists of Prova literals including Java calls, arithmetic expressions,
relations, and even CUT. The next example shows how CUT can be used in the literal guard.


a(X):qq(X).
a(X):s(X).

s(2).

Prova rule language 3.0

User’s Guide

May 2010

19

% This example shows how guards can be used to implement a "dynamic CUT".
% The CUT in the guard is dynamically spliced into the start of the target rule body.
% As qq(X) has no solutions, the CUT prevents further backtracking to the second rule for a(X):s(X),
% that would have yielded a solution but does not due to that dynamic CUT.
p2(X):
a(X) [!].
:solve(p2(X2)).

If the CUT was included as part of the p2(X) rule after a(X) like in the following version, the a(X)
rule including qq(X) would have failed but the second rule would have provided one solution: ‐2.

p2(X):
a(X),!.

Prova guards play even a more important role in message and event processing as they allow the
received messages to be examined before they are irrevocably accepted, see the details in Guards in
message processing.

1.10. Workflows and business processes

Prova offers a fairly unique combination of processing techniques, including workflows, reactive
messaging, event processing and elements of functional programming. The idea is to give the users a
great freedom for building distributed agents using a holistic approach that does not force them to a
particular methodology.

The following syntactic and semantic instruments in Prova 3.0 capture the basics and offer
unique advanced features for implementing workflows and business processes.
inherent non‐determinism for defining process divergences;
Reactive messaging;
Concurrency support, including partitioned and non‐partitioned thread pools;
built‐in predicate spawn for running tasks;
process join
predicate join
reaction groups combining event processing with workflows (see event driven gateways and
Event processing using reaction groups);
support for dynamic event channels;
guards
This Section discusses the Prova support for workflows, in particular, both simple and
sophisticated logic‐based predicate gateways.

1.10.1. Simple gateways

Prova includes a very simple mechanism (inspired by Join‐calculus) for creating diverging
branches (parallel gateway) and process join points (inclusive gateways). Consider the following
simple workflow presented in the BPMN notation.

Prov

The
impleme

:eva

proc
pr
%
se
%
%
in
%
in
%
%
commun
%
fo

fork_
%
se
rc
fo
fork_
%
se
rc
%
jo

fork_
%
se
rc
%
jo
fork_
%
se
rc

va rule langu

following c
ent this work

al(process_jo

cess_join() :
rintln(["====
%
This messag
endMsg(XID,s
%
The result of
%
Create a join
nit_join(XID,jo
%
Create a join
nit_join(XID,jo
%
This will cre
%
Note that Pr
icate

% with other
ork_a_b(XID).

_a_b(XID) :
% Task a
endMsg(XID,s
cvMsg(XID,self
ork_c_d(XID).

_a_b(XID) :
% Task b
endMsg(XID,s
cvMsg(XID,self
%
Tell the join
oin(Me,XID,joi

_c_d(XID) :
% Task c
endMsg(XID,s
cvMsg(XID,self
%
Tell the join
oin(Me,XID,joi
_c_d(XID)
:
% Task d
endMsg(XID,s
cvMsg(XID,self

uage 3.0

code from p
kflow.

in()).

======proce
ge
signals the s
elf,0,start,[]),

f the above is
n predicate jo
oin_1,[c(_),b(_)
n
predicate jo
oin_2,[c(_),d(_)
eate
two para
rova
does not

r agents async

elf,0,reply,a(1
lf,Me,reply,a(1

elf,0,reply,b(1
lf,Me,reply,b(1
join_1
that a
in_1,b(1)).

elf,0,reply,c(1
lf,Me,reply,c(1
join_1
that a
in_1,c(1)).

elf,0,reply,d(1
lf,Me,reply,d(1

rocess_join.p

ess_join=====
start
of the co

that we now
in_1 with the
)]),

in_1 with the
)]),

llel processing
run
them in s

chronously eff

1),corr_a),
1),corr_a),

1),corr_b),
1),corr_b),
new pattern

1),corr_c),
1),corr_c),
new pattern

1),corr_d),
1),corr_d),

User’s Gu

prova uses

======"]),
onversation. T

have the con
e
list of requir

e list of requir

g streams.
separate thre

ffectively allow

is ready

is ready

uide

the built‐in

The engine in

versationid X
red
input patt

red input patt

eads but they

wing for para

predicates

itializes the c

XID initialised
erns
in the las

erns in the las

are independ

llel processing

M

init_join and

conversation

d.
st argument.

st argument.

dent and can

ng.

May 2010

20

d join to

id XID.

Prova rule language 3.0

User’s Guide

May 2010

21

join(Me,XID,join_2,d(2)).

% The following rule will be invoked by join once all the inputs are assembled.
join_1(Me,XID,Inputs) :
join(Me,XID,join_2,c(2)),
println(["Joined ",join_1," for XID=",XID," with inputs: ",Inputs]).

% The following rule will be invoked by join once all the inputs are assembled.
join_2(Me,XID,Inputs) :
println(["Joined ",join_2," for XID=",XID," with inputs: ",Inputs]).

% A testing harness catchall reaction for printing all messages.
rcvMsg(XID,Protocol,From,Performative,[X|Xs],Extra) :
println([Performative,[X|Xs],Extra]).
This code prints:

==========process_join==========
reply for conversationid prova:1: [a,1],corr_a
reply for conversationid prova:1: [b,1],corr_b
Joined join_1 for XID=prova:1 with inputs: [[b,1], [c,1]]
reply for conversationid prova:1: [c,1],corr_c
Joined join_2 for XID=prova:1 with inputs: [[c,2], [d,2]]
reply for conversationid prova:1: [d,1],corr_d

In the example, all tasks are modeled as messages sent to self, i.e., the engine itself, and
accepting the same message in the body of the same rule. This could be replaced by either in‐body
task execution, spawning a computation using spawn or sending a request message to an agent and
accepting a reply with the results. Remember that rcvMsg does not block the current thread but
waits for a message asynchronously, while keeping all the current data in the transparently created
closure. Parallel branching is implemented by simply creating a predicate with multiple clauses and
processing the relevant tasks asynchronously.
Now back to the init_join and join predicates. The former initializes a join (an inclusive gateway)
by passing it (1) the conversation‐id on which the join will be processed, (2) the exit predicate
invoked when the join conditions are verified and (3) a list of join terms representing tokens that
need to become available for the exit predicate of the join to fire. It is entirely possible to specify join
terms containing arbitrary free variables, including anonymous variable '_'.
When processing reaches a point in the workflow where the join condition can be
communicated to the engine, the code uses the join predicate that accepts the name of the agent,
the conversation‐id, the name of the join, and the concrete join term that corresponds to a newly
available join token. When the engine detects that all join terms are available, it processes the exit
goal for the predicate corresponding to the join name specified at initialization (see clauses for
join_1 and join_2).

1.10.2. Eventdriven gateways (edBPM)

Prova supports ebPBM‐style of agent programming and, in fact, offers much more with the
concept of reaction groups. One could argue that an event‐driven gateway is a simplified version of a
reaction group. Consider the following variation on the example in the previous section.

Prov

Wha
dependi
detected
The
impleme

fork_
@
rc
se
rc
fo
fork_
@
rc
se
rc
%
jo
fork_
@
rc

An O
behavio
event ar
happens
from the
are actu
Howeve
annotat
run_b ar
only ext
situation
The
this spe
workflow
imposes
timeout

fork_

va rule langu

at changed
ing on wheth
d, the other
following m
ented using a

_a_b(XID) :
@group(g)
cvMsg(XID,Pro
endMsg(XID,s
cvMsg(XID,self
ork_c_d(XID).

_a_b(XID) :
@group(g)
cvMsg(XID,Pro
endMsg(XID,s
cvMsg(XID,self
%
Tell the join
oin(Me,XID,joi
_a_b(XID)
:
@or(g)
cvMsg(XID,Pro

OR reaction
r such that t
rrives, due t
s to be the a
e group that
ually not inte
er, there are
ion declares
rrives, the g
tension of t
n.
beauty of th
ecification, t
w, event pat
s a 10 secon
channel.

_a_b(XID) :

uage 3.0

here is the
her the even
alternative b
modified first
a reaction gr

otocol,From,c
elf,0,reply,a(1
lf,Me,reply,a(1

otocol,From,c
elf,0,reply,b(1
lf,Me,reply,b(1
join_1
that a
in_1,b(1)).

otocol,From,o

group gives
the group co
to the sema
alternative re
t is used for
erested in co
e sub‐branch
s them to be
roup as a wh
he workflow

his approach
ranslating t
ttern, or mor
ds timeout

e exclusive c
nt run_a or ru
branch is imm
t half of the
roup.

command,run
1),corr_a),

1),corr_a),

command,run
1),corr_b),

1),corr_b),
new pattern

or,_).

the two me
ollectively w
antics of OR,
eaction, disa
defining the
ommon actio
hes following
e part of the
hole disappe
w. Note that

h is the linea
o the ease
re general re
on the grou

User’s Gu

choice that
un_b arrives
mediately di
above exam

n_a),

n_b),

is ready

essage hand
waits for eith
, the group
appears. The
e semantics o
on logic so th
g either of
reaction gro
ears but the
t we use a

rity of exten
of authorin
eactive behav
p, adds a @

uide

the environ
s first. Moreo
sabled so th
mple shows h

lers for even
her of the tw
as a whole
e @or annot
of the group
his reaction
the reaction
oup with a l
closure of t
Prova OR re

nsible seman
ng and main
vior. For exa
@not annotat

nment impo
over, as soon
at only one b
how the eve

nts run_a an
wo events to
is terminate
ated reactio
p as a whole
is not follow
ns annotated
ogical name
he fired bran
eaction grou

tic variants t
ntainability o
mple, the fo
tion on the r

M

oses on the
n as either o
branch can p
ent‐driven ga

nd run_b a c
o arrive. Whe
ed so that w
on is the exit
. In this inst
wed by anyth
d with @gro
e g. So once
nch continue
up to model

that can be a
of the desig
ollowing mod
run_b chann

May 2010

22

process
f them is
proceed.
ateway is

collective
en either
whatever
t channel
ance, we
hing else.
oup (this
run_a or
es as the
an XOR

added to
gn, be it
dification
nel and a

Prova rule language 3.0

User’s Guide

May 2010

23

@group(g)
rcvMsg(XID,Protocol,From,command,run_a),
sendMsg(XID,self,0,reply,a(1),corr_a),
rcvMsg(XID,self,Me,reply,a(1),corr_a),
fork_c_d(XID).
fork_a_b(XID) :
@group(g) @not
rcvMsg(XID,Protocol,From,command,run_b).
fork_a_b(XID) :
@or(g) @timeout(10000)
rcvMsg(XID,Protocol,From,or,_).
fork_a_b(XID) :
@or(g)
rcvMsg(XID,Protocol,From,timeout,_),
sendMsg(XID,self,0,reply,b(1),corr_b),
rcvMsg(XID,self,Me,reply,b(1),corr_b),
% Tell the join join_1 that a new pattern is ready
join(Me,XID,join_1,b(1)).

If run_a arrives before the timeout, it remains the only branch as in the previous case. If neither
run_a nor _run_b arrive before the timeout, the timeout channel proceeds with the branch with task
b. Prova 3.0 includes a large collection of annotations for reaction groups that really help with
designing very sophisticated workflows.

1.10.3. Predicate gateways

It is easy to see why simple joins may be quite limiting. Imagine you need to analyze what join
tokens are available to the join and make a decision using logical reasoning. Furthermore, if the final
join conditions are not yet achieved, it would be great if we could re‐initialize the join, if some other
conditions are verified. There are known workflow patterns (for example, Structured Discriminator)
cataloged by van‐der‐Aalst that benefit from such features.
The following example predicate_join_concurrent.prova uses the enhanced version of the same
workflow framework complemented by built‐in predicates init_predicate_join and predicate_join.

:eval(predicate_join_concurrent()).

predicate_join_concurrent() :
println(["==========predicate_join_concurrent=========="]),

for([0,2],I),
sendMsg(XID,async,0,request,a()),
rcvMsg(XID,async,Me,reply,a()),

% Initialise the predicate join:
% XID is conversation id;
% join_1 is the exit predicate that must be unique for each join;
% join_predicate_1 is the join conditions;
% i1..i3 are the required tokens.
init_predicate_join(XID,join_1,[i1(),i2(),i3()],join_predicate),
println([XID]),
% This will create three parallel processing streams.
loop_a_body("Worker",XID).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%%%%

% Three branches executing three parallel activities %
% representing a divergence in the process %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%%%%

Prova rule language 3.0

User’s Guide

May 2010

24


loop_a_body(Partner,XID) :
switch_thread(a),
println(["Branch 1 complete"]),
predicate_join(XID,join_1,i1(),[Partner]).
loop_a_body(Partner,XID) :
switch_thread(b),
println(["Branch 2 complete"]),
predicate_join(XID,join_1,i2(),[Partner]).
loop_a_body(Partner,XID) :
switch_thread(c),
println(["Branch 3 complete"]),
predicate_join(XID,join_1,i3(),[Partner]).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%%

% Rules representing the exit branches of the JOIN %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%%

% The following rule will be invoked by join once ONE input for join_1 has arrived.
% This is governed by the first join_predicate rule.
join_1(s(0),XID,Count,Inputs,[Partner]) :
println(["Joined for XID=",XID," at state 's(0)' with inputs: ",Inputs]),
% Report to the test case runner that this step is complete
sendMsg(XID,self,0,job_completed,[]).
% The following rule will be invoked by join once all inputs for join_1 have arrived
% This is governed by the second join_predicate rule.
join_1(reset,XID,Count,Inputs,[Partner]) :
println(["Joined for XID=",XID," at state 'reset' with inputs: ",Inputs]),
sendMsg(XID,self,0,body_a_complete,[]),
% Report to the test case runner that this step is complete
sendMsg(XID,self,0,job_completed,[]).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%

% Rules establishing when the predicate join reaches appropriate states %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%


% The rule for determining a join condition. Note that the state must have format "s(Number)".
join_predicate(XID,join_1,s(0),Msg,Waiting,Count,Complete,Params) :
1=Complete.size().
% The rule for determining a reset condition. When this condition is reached, the JOIN is reset.
% However, we also offer a chance to define an optional JOIN exit branch when the reset is reached
% (see rule 2 for the join_1 predicate).
join_predicate(XID,join_1,reset,Msg,Waiting,Count,Complete,Params) :
0=Waiting.size().

switch_thread(A) :
sendMsgSync(XID,task,0,switch,[A]),
rcvMsg(XID,task,From,switch,[A]).

%%%%%%%%%%%%%%%%%
% A NOOP worker %
%%%%%%%%%%%%%%%%%

rcvMsg(XID,Protocol,From,request,[X|Xs]) :
println([rcvMsg(XID,Protocol,From,request,[X|Xs])]),

Prova rule language 3.0

User’s Guide

May 2010

25

sendMsg(XID,Protocol,0,reply,[X|Xs]).

% A testing harness catchall reaction for printing all messages.
rcvMsg(XID,Protocol,From,Performative,[X|Xs]) :
println([Performative,[X|Xs]]).

% A testing harness catchall reaction for printing all messages.
rcvMsg(XID,Protocol,From,Performative,[X|Xs],Extra) :
println([Performative,[X|Xs],Extra]).

1.11. Functional programming extensions

Functional programming is all about being able to compose functionality. This ability is related to
the glorious category theory that emphasizes maps (arrows) over objects. Being slightly
controversial, I would claim that functional programming languages should primarily strive for that
simplicity to compose, i.e., linearly augment things, rather than getting bogged down in difficult to
read syntax and detailed refinement.
The Prova extension for functional programming attempts to do away with the proliferation of
syntactical constructs and distill that easy composability. What has been done so far is by no means
complete but it offers a number of immediately useful features such as
single‐ and multi‐valued functions, the latter offering direct support for non‐determinism
and backtracking;
functional composition with the extended derive built‐in;
partial evaluation;
lambda functions;
monadic functions;
monadic bind using a composition of map and join;
maybe, list, state, and tree monads as part of the provided library with easy extensibility.
In order to utilize the functional extensions in Prova, you need to consult the utility library
functional.prova from your rulebase.

% This assumes the indicated path location for the library
:‐ eval(consult('rules/reloaded/functional.prova')).
From the source code Prova distribution, you can also run the tests in
ProvaFunctionalProgrammingTest.java to find out what is available.

1.11.1. Representing functions

Prova allows the user to define functions using facts and rules obeying a number of simple

conventions.

1. Functions are defined via rules for arity 2 predicates whose predicate symbol then becomes
the function symbol.
2. The first argument is the input and the second argument is the output of the function.
3. If the input (output) is a simple term, this corresponds to a one input (output) parameter

function.

4. Otherwise, the input (output) is a Prova list, corresponding to multiple input (output)
parameters.
5. The only exceptions to rules 4 and 5 are Prova lists beginning with strings maybe, list, state
or tree, in which case those are treated as a single input or output parameter.
Consider some examples.

double(X,XM) :
XM=2*X.

add([A,B],Z) :

Prova rule language 3.0

User’s Guide

May 2010

26

Z=A+B.

putState([V,S],state([V,V])).

plusminus(X,X).
plusminus(X,XM) :
XM=X.

The first two of them should be self‐explanatory while the third generates a state monad
representation with equal value and state. The last example is non‐deterministic and defines a multi‐
valued function that takes a number and either echoes it back or returns its negation.

1.11.2. Functional composition

Remember that everything about functions is about composition. Prova currently offers two
mechanisms for composing functions: simple composition and monadic composition.
In both cases, Prova currently requires a built‐in predicate derive to be used for executing
functional pipelines. The functional flavor of derive takes a single parameter, which is a Prova list
with exactly three elements:
1. functional pipeline,
2. input,
3. output.
Let us see some examples.

% Instantiates L1=6
derive([double,3,L1])

% Instantiates L2=4
derive([[add(1)],3,L2])

% Instantiates L3=8
derive([[double,add(2)],3,L3])

The functional pipeline is represented by a single function (double in the first example), a
partially evaluated function (add(1) in the second example, note the required square brackets
around it), a lambda function described later, or a list of those, executed in sequence (double,add(1)
in the last example).

The input and output can be pretty much any simple or compound Prova terms. In the case
when output is specified, Prova will unify the results with the provided pattern, only succeeding if
there is a match, which allows for easy constraint solving using normal backtracking if there is non‐
determinism encountered along the way, perhaps due to multi‐valued functions.

1.11.3. Lambda functions

We need one more building block to fully appreciate functional composition, viz., lambda
functions. Lambda functions can be used directly in function pipelines for specifying a function right
there as opposed to using pre‐defined functions defined using rules. One advantage is immediate
flexibility and the ability to define and pass around lambda expressions even between distributed
agents. A more direct advantage is the ability to obtain access to data available from the previous
stage of the pipeline and use them precisely in the lambda expression, as opposed to the default
splicing of that data at the end of the current function arguments.
The syntax is simple:

lambda(Var,Function)

Here Var is a variable that gets instantiated to the data coming from the input. Function is again
either a single (possibly, partially evaluated) function or a list of functions representing a nested
functional pipeline. Note that Function is not required to include Var. Note that lambda variables are

Prova rule language 3.0

User’s Guide

May 2010

27

visible and can be used in all subsequent functions in the enclosing pipeline which is a great way to
pass the data downstream.

% Two equivalent forms, without and with lambda
% Both instantiate L2=4
derive([[add(1)],3,L2])
derive([[lambda(A,add(1,A))],3,L2])

% An example of passing data downstream
% This instantiates: V3=9, V2=4, V1=2, C1=13
derive(
[[lambda(V1,double(V1)),lambda(V2,double(3)),lambda(V3,add(V2,V3))],2,C1]
)

1.11.4. Simple functional composition

In the case of simple composition, the functional pipeline is composed of functions operating on
the totality of the values being passed between them. To appreciate the power of functional
composition, we would like to pass between functions compound data as opposed to single values.
Consider the following function.

duplicate(X,[X,X]).

Whatever the X value, the function creates a two‐element list with two identical copies of the
same value (or structure, if X is also a list). Following is an example of its use.

% Returns XX=6
derive(
[[duplicate,add],3,XX]
)

What happens here? First, the simple value 3 is transformed into the list [3,3]. Then these two
numbers (3 and 3) get shipped as the first two parameters to the function add, returning the result.
The apparent inflexibility of always attaching the computation results to the end of the next
function's arguments is nicely resolved by using lambda functions.

% Given this definition
second_degree([A,B,C,X],R) :
R=A*X*X+B*X+C.

% Splice 3 as both A and B
% This instantiates: Z=3, XX=7
derive(
[[lambda(Z,second_degree(Z,Z,1,1))],3,XX]
)

Now finally if the input is a list, the lambda function needs to accept more than one variable by

declaring its input as a list.

% This instantiates: A=3, B=3, L11=7
derive(
[[duplicate,lambda([A,B],second_degree(A,B,1,1))],3,L11]
)
% This also works the same way
derive(
[[lambda([A,B],second_degree(A,B,1,1))],[3,3],L11]
)

Now we are completely ready for FizzBuzz, functional Prova style. We are going to show three
ways to do the same thing and later do the same again using the List monad. Note that for is a multi‐

Prova rule language 3.0

User’s Guide

May 2010

28

valued function that non‐deterministically returns all integers between 1 and 100 (its definition is
very simple and is part of the utility library functional.prova). prtln is a function that echoes back its
input while printing it followed by newline.

:solve(
derive(
[[for(1,100),fizzbuzz(3,5),prtln],[],X]
)
).
:solve(
derive(
[[for(1,100),lambda(I,fizzbuzz(3,5,I)),prtln],[],X]
)
).
:solve(
derive(
[[for,lambda(I,fizzbuzz(3,5,I)),prtln],[1,100],X]
)
).

% fizzbuzz: (M1,M2,I)>R
fizzbuzz([M1,M2,I],R) :
C1 = I mod M1,
C2 = I mod M2,
fizzbuzz(C1,C2,I,R).

fizzbuzz(0,0,_,fizzbuzz) :!.
fizzbuzz(0,_,_,fizz) :!.
fizzbuzz(_,0,_,buzz) :!.
fizzbuzz(_,_,I,I).

1.11.5. Monadic functional composition

In the case of monadic composition, the functional pipeline passes around monadic data and is
composed of functions operating on data in some form "contained" in the monadic data. Monadic
composition in Prova is done by pattern‐matching the data passed between functions in the
functional pipeline.

To clarify, this approach is not directly comparable to the one used in functional programming
languages. The latter is based on strong typing enforced at compile‐time. Prova is obviously quite a
bit more dynamic so the approach used here is data‐driven. In the following discussion, we assume
minimal understanding of what monads are about (see, for example, this compact post:
http://www.haskell.org/haskellwiki/Monads_as_containers).
The core idea is that monadic functions produce Prova terms matching pre‐defined patterns
known to the functional framework.

1.12. Builtins reference

This Section provides a complete reference for the built‐in primitives in the Prova language.

assert/1 (i)

This builtin predicate takes a Prova list as the only argument and adds it to the rulebase run by
the engine as a fact (a rule without a body) for the predicate symbol corresponding to the first
element in the supplied list. Remember that in Prova, a standard Prolog‐like compound term
f(t1,...,tN) is a syntactic equivalent of a list [f,t1,...,tN], so we see that the predicate symbol
corresponds to the functor f of the compound term. Note that Prova facts can contain free variables.

Prova rule language 3.0

User’s Guide

May 2010

29

TL2 = javax.swing.JLabel("Results"),
TL2.setAlignmentX(java.awt.Component.CENTER_ALIGNMENT),
assert(label2(TL2)),

The asserted facts can be queried in the same way as the regular facts that were part of the
rulebase at the time the Prova engine started.

label2(TL2),
unescape("\ntest_spawn 2 finished.",Text),

The asserted facts can removed using retract/1 or retractall/1 builtin predicates.

Comp = Event.getComponent(),
% Remember where the popup occurred
retractall(selected_component(_)),
assert(selected_component(Comp)),

asserta/1 (i)

This builtin predicate takes a Prova list as the only argument and adds it in front of any other
facts
 for the same predicate to the rulebase run by the engine as a fact (a rule without a body) for
the predicate symbol corresponding to the first element in the supplied list. Remember that in
Prova, a standard Prolog‐like compound term f(t1,...,tN) is a syntactic equivalent of a list [f,t1,...,tN],
so we see that the predicate symbol corresponds to the functor f of the compound term. Note that
Prova facts can contain free variables.
The following example demonstrates.

:solve(test008(X,Y)).

test008(X,Y) :
assert(symmetric(f)),
% The fact below will be added higher than the one above
asserta(symmetric(g)),
symmetric(X),
println(["Rule A1: symmetric",X]," "),
symmetric(Y),
println(["Rule A2: symmetric",Y]," ").
test008(X,Y) :
assert(dual(h,a)),
assert(dual(h,_)),
asserta(dual(h,f)),
retractall(dual(_,a)),
dual(X,Y),
println(["Rule B: dual",X,Y]," ").
The example prints:

Rule A1: symmetric g
Rule A2: symmetric g
X=g, Y=g
Rule A2: symmetric f
X=g, Y=f
Rule A1: symmetric f
Rule A2: symmetric g
X=f, Y=g
Rule A2: symmetric f
X=f, Y=f
Rule B: dual h f
X=h, Y=f

Prova rule language 3.0

User’s Guide

May 2010

30

Rule B: dual h <50>
X=h, Y=<50>

attach/3 (i,i,io)

This predicate appends the Prova list in the second argument to the Prova list in the first
argument, returning the result in the third. Since Prova lists are held in arrays, the code can
concatenate very large lists.
In the first argument,
the list can only have a tail that is a list‐‐not a free Prova variable,
the tail list is allowed to have a tail but this tail is ignored and not copied to the result List.
In the second argument,
the list can only have a tail and the tail is attached to the end of the result list.
Here are some examples.

:solve(attach([1|L],[2,3],X)).

:solve(attach([1|[1,L]],[2,3],X)).

:solve(attach([1|[1|L]],[2,3],X)).

:solve(attach([1|[1|L]],[2,3|Z],X)).

This prints:

no
L=L, X=[1,1,L,2,3]
L=L, X=[1,1,2,3]
L=L, Z=Z, X=[1,1,2,3|Z]

Here is a classic example of tower of Hanoi puzzle in Prova, also making use of Prova tabling
using the cache built‐in predicate.

:solve(move(4,left,mid,right,Plan)).

move(0,_,_,_,[]):!.
move(N,A,B,C,Plan) :
NM1=N1,
cache(move(NM1,A,C,B,PlanL)),
cache(move(NM1,B,A,C,PlanR)),
attach(PlanL,[[A,C]|PlanR],Plan).

bound/1 (i)

This predicate fails if the supplied argument is a free Prova variable or a Prova list containing
Prova variables. Otherwise, it succeeds.
The following fragment shows how to test if the argument supplied to the clause is bound.

access_data(Type,ID,Data,CacheData) :
% Attempt to retrieve bound data
bound(ID),
Data=CacheData.get(ID),
% Success, Data (whatever object it is) is returned
!.

byte_stream/2 ([i,i],o)

This predicate encodes either an input string or a Java ByteArrayOutputStream using a supplied
charset name (see IANA Charsets) and returns a Java ByteArrayInputStream wrapping the resulting

Prova rule language 3.0

User’s Guide

May 2010

31

byte array. The predicate is structured as a Prova function (see Functional programming extensions).
In the first argument, it accepts a Prova list with two input parameters: (1) an input string or a
ByteArrayOutputStream and (2) a charset name. The second argument is the produced
ByteArrayInputStream.

The following example compresses a Java String, stores to disk, and retrieves it back.

:solve(test_byte_stream(toto)).

test_byte_stream(Result) :
println(["==========byte_stream=========="]),
byte_stream(["toto","UTF8"],BAIS),
File=java.io.File.createTempFile("prefix","suffix"),
FO=java.io.FileOutputStream(File),
ZFO=java.util.zip.GZIPOutputStream(FO),
copy_stream(BAIS,ZFO),
println(["Compressed file created."]),

FI=java.io.FileInputStream(File),
ZFI=java.util.zip.GZIPInputStream(FI),
BAOS=java.io.ByteArrayOutputStream(),
copy_stream(ZFI,BAOS),
byte_stream([BAOS,"UTF8"],Result),
println(["The original string read from the compressed file: ",Result]).

The following test from ProvaBuiltins1Test.java shows how this is run from Java.

@Test
public void byte_stream_and_copy_stream() {
final String rulebase = "rules/reloaded/byte_stream.prova";
final int[] numSolutions = new int[] {1};

ProvaCommunicator prova = new
ProvaCommunicatorImpl(kAgent,kPort,rulebase,ProvaCommunicatorImpl.SYNC);
List solutions = prova.getInitializationSolutions();
org.junit.Assert.assertEquals(1,solutions.size());
org.junit.Assert.assertEquals(numSolutions[0],solutions.get(0).length);
}

cache/2 (i)

This builtin predicate provides support for tabling in Prova. It enables the literal in its only
argument for caching. The current implementation in Prova 3.0 keeps the cached answer set for the
literal indefinitely.

The following example runs the Hanoi Tower puzzle for 20 disks, which would have been
prohibitively long if the cache predicate was not used. Naive implementations in Haskell or Scala also
suffer unless tabling is explicitly added.

:solve(move(20,left,mid,right,Plan)).

move(0,_,_,_,[]):!.
move(N,A,B,C,Plan) :
NM1=N1,
cache(move(NM1,A,C,B,PlanL)),
cache(move(NM1,B,A,C,PlanR)),
attach(PlanL,[[A,C]|PlanR],Plan).

Tabling also helps to avoid infinite looping in code as the example below shows.

:solve(reach(X)).

edge(a,b).
edge(c,d).
edge(d,c).

Prova rule language 3.0

User’s Guide

May 2010

32


reach(a).
reach(A) :
edge(A,B),
cache(reach(B)).

This returns only one solution X=a.

capture_enum/2 ([i,i],io) ND

This non‐deterministic predicate enumerates regular expression groups. It is structured as a
multi‐valued Prova function (see Functional programming extensions). In the first argument, it
accepts a Prova list with two input parameters: an input string and a regular expression. It produces
independent solutions, one each for each possible answer in the second argument. This creates a
choice point with as many non‐deterministic branches as there are tokens in the input stream.
The following example shows how this works.

:solve(test017(Groups)).

test017(Groups) :
Text="A doodle is a doddle",
% nondeterministically enumerate regular expression groups
capture_enum([Text,"(d?)(dl)"],Groups).

This returns:

Groups=[,dl]
Groups=[d,dl]

clone/2 (i,io)

This builtin predicate implements a "unidirectional unification". It creates fresh variables for the
first term and then unifies it with the second supplied term so that the result is available in the
second argument while the first argument is left unmodified. For example,

:solve(test_clone(must_match,add)).

test_clone(B,FunX) :
clone(lambda(A,[add,A,4]),lambda(1,[FunX|ArgXs])),
% At this point, A is still a free variable, but FunX is bound to a string 'add'
println([FunX,ArgXs,B]," "),
println(lambda(A,[add,A,4])," "),
% must_match unifies with a constant A successfully
% Not we cannot use A as the head variable, otherwise clone cannot match must_match with 1
B = A.

The code above prints the following.

add [1,4] must_match
lambda <12> [add,<12>,4]
yes

One use of this builtin is to map lambda expressions over lists (and other monads). The copy of
the lambda function needs to be fresh on application to each list element, so clone/2 achieves this
by unifying the provided lambda expression in the first argument with a standard template in the
second argument, applies the result in the second argument to the current element using the builtin
derive/3 and continues processing the remainder of the list with the original unmodified lambda
expression.

map([lambda(A,[Fun|Args]),list(X|As)],[B|Bs]) :
!,
clone(lambda(A,[Fun|Args]),lambda(X,[FunX|ArgXs])),
derive([FunX,ArgXs,B]),
map([lambda(A,[Fun|Args]),As],Bs).

Prova rule language 3.0

User’s Guide

May 2010

33

concat/2 (i, io)

This builtin predicate takes a Prova list and iterates over the list elements, by converting them to
Java strings, concatenating them, and unifying the result with the second argument. If the second
argument is not a free variable, the unification between the concatenation result and this argument
may fail which then fails the builtin.

% No solutions here
r(AB) :
concat([a,b], AB),
concat([a,c], AC),
concat([a,c], AB),
% This line is (correctly) unreachable in Prova 3.0
println(["1: ",AB," = ",AC]).

copy/2 (i,i)

This predicate takes the character stream from a Java Reader and writes it using a Java Writer. IO
exceptions are wrapped in a RuntimeException.
The following code uses a StringWriter to capture the stream in a buffer. A mobile agent
functionality is implemented by sending a rulebase fragment to another agent that then consults the
rulebase dynamically and runs a goal.

upload_mobile_code(XID,Remote,File) :
fopen(File,Reader),
Writer = java.io.StringWriter(),
copy(Reader,Writer),
SB = Writer.getBuffer(),
sendMsg(XID,esb,Remote,upload,consult(SB)).
...
rcvMsg(XID,Esb,From,upload,consult(SB)) :
iam(Me),
consult(SB),
% The clause for worker/2 is defined in the code that the Manager uploads to this worker
worker(XID,From).

copy_stream/2 (i,o)

This predicate copies a Java ByteArrayInputStream to a Java ByteArrayOutputStream. The
predicate is structured as a Prova function (see Functional programming extensions), with the first
argument as input and the second argument as output.
The following example compresses a Java String, stores to disk, and retrieves it back.

:solve(test_byte_stream(toto)).

test_byte_stream(Result) :
println(["==========byte_stream=========="]),
byte_stream(["toto","UTF8"],BAIS),
File=java.io.File.createTempFile("prefix","suffix"),
FO=java.io.FileOutputStream(File),
ZFO=java.util.zip.GZIPOutputStream(FO),
copy_stream(BAIS,ZFO),
println(["Compressed file created."]),

FI=java.io.FileInputStream(File),
ZFI=java.util.zip.GZIPInputStream(FI),
BAOS=java.io.ByteArrayOutputStream(),
copy_stream(ZFI,BAOS),
byte_stream([BAOS,"UTF8"],Result),

Prova rule language 3.0

User’s Guide

May 2010

34

println(["The original string read from the compressed file: ",Result]).
The following test from ProvaBuiltins1Test.java shows how this is run from Java.
@Test
public void byte_stream_and_copy_stream() {
final String rulebase = "rules/reloaded/byte_stream.prova";
final int[] numSolutions = new int[] {1};

ProvaCommunicator prova = new
ProvaCommunicatorImpl(kAgent,kPort,rulebase,ProvaCommunicatorImpl.SYNC);
List solutions = prova.getInitializationSolutions();
org.junit.Assert.assertEquals(1,solutions.size());
org.junit.Assert.assertEquals(numSolutions[0],solutions.get(0).length);
}

consult/1 (i)

This builtin predicate dynamically "consults" (imports) a Prova rulebase from either of the
following provided as its only input:
a String with file pathname, resource, or URL, in this order.
a pre‐constructed BufferedReader.
a StringBuffer.
This predicate is often used in rulebases that need to import another rulebase, typically with
some common library rules. This requires running a goal for a meta‐predicate eval to execute this
built‐in. Any goals contained in the consulted rulebase are executed synchronously, before
returning.

:eval(consult('rules/reloaded/functional.prova')).

element/2 (io,i) ND and element/3 (io,io,i) ND

The element predicate is a non‐deterministic predicate that matches the pattern provided in the
first argument with a list of elements. The list can be a Prova list, a Java List, or even a Java Iterator.
Consider this example:

% Shows that element/2 can use Java typed and listbased patterns to find matching elements.

:solve(element_matching([Integer.N,X])).

element_matching([Integer.N,X]) :
L = java.util.ArrayList(),
L.add([X,2]),
L.add([Integer.I,Double.D]),
L.add([2.14,Double.D]),
L.add([3,"toto"]),
element([Integer.N,X],L).

% The "old" Prova returns:
% ,2
% ,
% 3,toto
% The correct first solution should be 2,2

The 'old' Prova does not unify the elements inside a Java List so it does not correctly unify the
pattern [Integer.N,X] with the list element [X,2]. The 'new' Prova correctly matches these elements
as they appear inside the Java list (see the final comment in the code above).
Here is an example of extracting one element at a time from a Java Iterator.

Iter = Model.listStatements(),
element(Stmt,Iter),

Prova rule language 3.0

User’s Guide

May 2010

35

The element/3 version is also returning (or accepting) the index of the matching element. The
example below demonstrates.

:solve(element_matching(Index,[Integer.N,X])).

element_matching(Index,[Integer.N,X]) :
L = java.util.ArrayList(),
L.add([X,2]),
L.add([Integer.I,Double.D]),
L.add([2.14,Double.D]),
L.add([3,"toto"]),
element(Index,[Integer.N,X],L).

% This returns:
% Index=0, N=2, X=2
% Index=1, N=java.lang.Integer., X=java.lang.Double.
% Index=3, N=3, X=toto

Implementation details

For this to work, we have added the ability for built‐in implementors to create a virtual predicate
and add virtual clauses to it. The clauses in the virtual predicate encapsulate alternative non‐
deterministic choices as possible unification targets for the literal that replaces the current goal as a
result of the built‐in processing. Hence unification works as normal and correctly matches the
element pattern with each list element in turn.

fail/0 ()

This predicate results in the immediate subgoal divergence (fail) that results in backtracking to
the previous non‐deterministic choice point in the goal evaluation tree. Note the Prova syntax for fail
that requires parentheses after fail like so: fail().
When the following code runs goals test(X) of test(id1), it does not return any solutions due to a
cut and fail in the first test clause. The goal test(id2) is successful.

test(id1) :
println(["Condition id1 being tested."]),
!,
fail().
test(id2) :
println(["Condition id2 being tested."]),
!.

findall/3 (i,i,o)

This predicate is a customary way in logic programming to accumulate all solutions to a goal
passed as a compound term (in Prova, it is just a list). It can be used among other things for
returning back all solutions to a goal communicated by a query message back to the requesting
agent.

% Reaction rule to a general queryref_acc
rcvMsg(XID,Protocol,From,queryref_acc,[ID,[X|Xs]]) :
findall([X|Xs],[X|Xs],L),
sendMsg(XID,Protocol,From,reply,[ID,L]).

The goal is passed in the second argument and the results are returned in the third argument
that currently must be initially a free variable. Normally, The first element X of the goal list [X|Xs] in
the inbound message will be bound to a predicate symbol, for which the solutions are sought. The
first argument provides the pattern specifying the actually desired elements to be added to a list
given the current goal solution. The call to findall instantiates the third argument to be a Java
ArrayList with all the results.

Prova rule language 3.0

User’s Guide

May 2010

36

We could modify the above example like this.

rcvMsg(XID,Protocol,From,queryref_acc,[ID,[X|Xs]]) :
findall(Xs,[X|Xs],L),
sendMsg(XID,Protocol,From,reply,[ID,L]).

This rule is different in that it does not include the goal predicate symbol, which is always the
first element of the list, but only includes the possible instantiations of the goal parameters in the
list rest Xs.

fopen/2 (i,o)

This predicate returns a BufferedReader in the second argument given a file name or a classpath
resource in the first argument. If the resource is not found, the built‐in will wrap the exception in a
RuntimeException.

The following code shows how a mobile agent functionality can be implemented by sending a
rulebase fragment to another agent that then consults the rulebase dynamically and runs a goal.

upload_mobile_code(XID,Remote,File) :
fopen(File,Reader),
Writer = java.io.StringWriter(),
copy(Reader,Writer),
SB = Writer.getBuffer(),
sendMsg(XID,esb,Remote,upload,consult(SB)).
...
rcvMsg(XID,Esb,From,upload,consult(SB)) :
iam(Me),
consult(SB),
% The clause for worker/2 is defined in the code that the Manager uploads to this worker
worker(XID,From).

for/2 (i,io) ND

The for predicate accepts a two‐element list with two integers denoting the loop bounds
[From,To] and non‐deterministically enumerates all intermediate values in the second variable that
is typically initially free. This predicate is structured in the way compatible with the Prova extensions
for functional programming.
Consider this example:

for([1,Max],I),
sendMsgSync(I,async,0,compute,I)

This fragment sends Max messages in new conversations with conversation‐id equal to I running

from 1 to Max.

free/1 (i)

This predicate succeeds if the supplied argument is a free Prova variable.
The following example test010.prova shows how the free predicate can be used in th eprocess of
constructing a sorted tree representation of an initially unsorted list.

% Sort the list provided
:solve(sort([7,3,6,1,4,5],L)).

insert_a(Val,t(Val,_,_)):!.
insert_a(Val,t(Val1,Tree,_)):
Val
!,
insert_a(Val,Tree).
insert_a(Val,t(_,_,Tree)):
insert_a(Val,Tree).

Prova rule language 3.0

User’s Guide

May 2010

37

instree([],_).
instree([H|T],Tree):
insert_a(H,Tree),
instree(T,Tree).

treemembers(_,T):
free(T),
!,
fail().
treemembers(X,t(_,L,_)):
treemembers(X,L).
treemembers(X,t(X,_,_)).
treemembers(X,t(_,_,R)):
treemembers(X,R).

sort(L,L1):
instree(L,Tree),
findall(X,treemembers(X,Tree),L1).

This returns:

L=[1, 3, 4, 5, 6, 7]

match/3 (i,io,io)

This predicate is quite unique and is typically used internally in the Prova extension for
functional programming (consulted from functional.prova).
What it does can be better illustrated with an example.

t(1,2).
t(2,3).
t(3,2).

:solve(test_match(S)).

test_match([X1,Y1,X2,Y2]) :
match(t(X0,2),t(X1,Y1),G),
match(t(X0,2),t(X2,Y2),G).

This example prints:

S=[1,2,3,2]

The idea behind match is that we should be able to iterate over facts (or rule heads) that match
the pattern supplied in the first argument. The iteration is executed by repeated invocation as
opposed to non‐deterministic choice. The second output‐only argument represents the actual fact as
it is stored in the rulebase rather than the result of its unification with the pattern in the first
argument. The third argument must be a free variable on first invocation but becomes instantiated
to a special handle that could be compared to an iterator, allowing for subsequent invocations of
match to return other matching facts. The match invocation finally fails when all the matching facts
are exhausted.

Moreover, the handle G in the above example can be used in the update built‐in predicate to
update the matching fact directly in place, without retracting it and asserting back a modified
version.

The functional extension hides match and update completely, allowing the user to simply define
a mutator function that takes the complex term corresponding to a fact literal and produces a
mutated version. This function is then passed to the functional pipeline that iterates over the facts
and updates them accordingly.
Here is an example.

o(1,2).
o(2,3).
o(3,2).

Prova rule language 3.0

User’s Guide

May 2010

38


add_snd([F(X,Y),D],F(X,YM)) :
YM=Y+D.

f(R) :
map([lambda(A,add_snd(A,1)),fact(o(X,2))],R).

:solve(f(R)).

:solve(o(S,3)).

The fact table o undergoes a change that increments its second argument for all facts matching
the pattern o(X,2). The second goal proves that all the o facts now have 3 in the second position and
the original order of the o facts is preserved.

R=[list,[o,1,3],[o,3,3]]
S=1
S=2
S=3

mklist/2 (i,io) ND

The mklist predicate assembles ("makes") a Prova list from a rest‐free Prova list and a variable
representing the list rest. The predicate follows the convention of the functional extensions to Prova
to enclose the inputs in a list in the first parameter and produce the resulting list in the second. The
first parameter is a list with two lists inside: a Prova list without a tail and a free variable that
represents a list tail. It outputs the list composed of the first list together with the supplied rest.
This example shows how this works.

:solve(mklist([[1,2],Xs],Xs)).

% This outputs:
% Xs=Xs, L=[1,2|Xs]

parse_list/2 ([i,i],io)

This predicate parses an input string using a supplied regular expression and returns a Prova list
with captured tokens. The predicate is structured as a Prova function (see Functional programming
extensions). In the first argument, it accepts a Prova list with two input parameters: an input string
and a regular expression. The second argument is the produced list of tokens. If this list is supplied as
input, it is unified against the output list tokens.
The following example demonstrates.

:solve(test_parse_list("abc:12a234",L)).

test_parse_list(In,L) :
parse_list([In,"(?:(\w*):|)(?\d*\w?)?(\d*)"],L).
test_parse_list(In,[T|Ts]) :
parse_list([In,"(?:(\w*):|)(?\d*\w?)?(\d*)"],[T|Ts]).
test_parse_list(In,L) :
parse_list([In,"(?:(\w*):|)(?\d*\w?)?(\d*)"],ttt).

This test prints the following solutions.

L=[abc,12a,234]
L=[abc,12a,234]

Prova rule language 3.0

User’s Guide

May 2010

39

parse_nv/2 ([i,i],[io,io])

This predicate parses an input string with name/value pairs using a regular expression to build
an array of names and and an array of values. It is structured as a Prova function (see Functional
programming extensions). In the first argument, it accepts a Prova list with two input parameters: an
input string and a regular expression. It returns results in another Prova list with two elements: an
array of names and an array of values.
The following example shows how this works.

:solve(test_parse_nv_1(Value1)).

:solve(test_parse_nv_2(Value2)).

test_parse_nv_1(Value) :
parse_nv(["j=12,s=tt","(?:(\w+)=(\w+),?)"],[Names,Values]),
element(Value,Values).

test_parse_nv_2(Value) :
parse_nv(["j=12,s=tt","(?:(\w+)=(\w+),?)"],[[N|Ns],[V|Vs]]),
element(Value,[V|Vs]).

This returns:

Value1=12
Value1=tt
Value2=12
Value2=tt

println/1 (i) or println/2 (i,i)

This builtin predicate accepts a Prova list as the first parameter and prints its elements to the
standard out channel using System.out.println(...). The second optional String parameter is a
separator that is inserted between each element.
The following example demonstrates.

:eval(msg()).

msg() :
println(["==========Communicator messaging test 001=========="]),

% This reaction is active indefinitely
rcvMult(XID,async,From,inform,{a>I}),
println(["Received",rcvMsg(XID,async,From,inform,{a>I})],": ").

% Given a stream of inbound messages, this agent prints:
% ==========Communicator messaging test 001==========
% Received: [rcvMsg,prova1,async,0,inform,{a>1}]
% Received: [rcvMsg,prova1,async,0,inform,{a>2}]
% ...

rcvMsg/5 (io,io,io,io,io)

This built‐in is used inside body rules for receiving messages (see a more in‐depth discussion in
Using reaction rules for receiving messages). Executing this built‐in creates a closure with all the
remaining sub‐goals in the current goal evaluation. The engine then immediately fails the current
branch in the goal exploration and backtracks to the previous choice point. The agent then waits for
an inbound message (without holding the current thread) that matches the pattern according to the
following arguments.
1. XID ‐ conversation id of the message;

Prova rule language 3.0

User’s Guide

May 2010

40

2. Protocol ‐ name of the message passing protocol;
3. Sender ‐ the agent name of the sender;
4. Performative ‐ the message type broadly characterizing the meaning of the message;
5. Payload ‐ a Prova list containing the actual content of the message.
The rcvMsg predicate is by far the most complex in Prova. To appreciate fully what can be done
using rcvMsg, the Reader will have to familiarize with Reactive messaging and Event processing.
rcvMsg is used both for accepting individual messages as in Example 1 below and for grouped
reactions as in Example 2.

Example 1

The following example rules/reloaded/simple_async.prova for each I between 0 and 2, inclusive,
sends a message over the async protocol and receives them on independent threads. sendMsgSync
is used for ensuring that messages are sent only after the goal is complete, so that all the
subsequent rcvMsg are ready to receive messages. XID gets instantiated by sendMsgSync each time
to a new value as it is initially a free variable.

:eval(simple_async()).

simple_async() :
for([0,2],I),

sendMsgSync(XID,async,0,request,a(I)),
rcvMsg(XID,async,Me,request,a(I)),
TH=java.lang.Thread.currentThread(),
println(["<",XID,"> ",I," on ",TH]).

Running it with "prova2.bat simple_async.prova" outputs three lines on random threads, for

example:

0 on Thread[pool8thread1,5,main]
2 on Thread[pool6thread1,5,main]
1 on Thread[pool7thread1,5,main]

Example 2

The following fragment from the flower shop use case in the project mule‐prova‐agents
demonstrates the use of a reaction group to collect exactly N initialization messages from N drivers
with the specified timeout. If all the drivers send the message within the specified timeout, the
group proceeds to positive reaction (second clause), otherwise, the timeout is detected and the
reaction in the third clause is invoked.

% Collect at least one update for each of N drivers prior to proceeding with requests
checkin_drivers(N) :
@group(init_drivers) @count(N,N)
rcvMsg(VanId,Protocol,VanId,update,gps_coordinates(Latitude,Longitude)).
checkin_drivers(N) :
@and(init_drivers) @timeout(10000)
rcvMsg(VanId,Protocol,VanId,and,[Updates]),
% All drivers have sent updates within the specified timeout
% GoGoGo! Ready to send orders now
send_orders().
checkin_drivers(N) :
@and(init_drivers)
rcvMsg(VanId,Protocol,VanId,timeout,[Updates]),
% Not all drivers have sent updates within the specified timeout, for now just print this
println(["Not all drivers have sent updates within the specified timeout"]).

rcvMult/5 (io,io,io,io,io)

This built‐in is a variant of the inline reaction predicate rcvMsg that allows for receiving multiple
inbound messages as opposed to a single message as in rcvMsg. As in the case of rcvMsg, the engine

Prova rule language 3.0

User’s Guide

May 2010

41

does not block the current thread and simply adds the continuation corresponding to the current
context goals and variables so that the execution picks up exactly where it left off when the
matching message is detected. In the case of rcvMult, the original reaction continues processing
other messages after the first one is detected.
The predicate is commonly used as an initiator of a workflow or event pattern detection. The
example below waits for a creation of a market and then proceeds to further processing in clauses
for the predicate server_1.

server(Limit) :
% Start detection on each new market
rcvMult(Market,Protocol,From,create,market(Market)),
server_1(Market,Limit).

By default, the rcvMult inline reaction exists until the Prova agent is terminated. However, there
is a way to abandon and purge this reaction.

client() :
sendMsg(XID,self,0,request,[19]),
assert(best(XID,0,0)),
branch(XID).

branch(XID) :
rcvMult(XID,self,Me,respond,[Service,Offer]),
println(["Received offer: ",Offer," from ",Service]),
choose(XID,BestService,Service,Offer).
branch(XID) :
spawn(XID,java.lang.Thread,sleep,1000L),
rcvMsg(XID,self,Me,return,Ret),
best(XID,BestService,BestOffer),
println(["Received best offer: ",BestOffer," from ",BestService]),
sendMsg(XID,self,0,eof,_).

choose(XID,BestService,Service,Offer) :
best(XID,BestService,BestOffer),
Offer>BestOffer,
retract(best(XID,BestService,BestOffer)),
assert(best(XID,Service,Offer)).

In the above example from hohpe_dynamic_discovery.prova, the client sends a request for
proposals with a payload containing some test data (19). It then asserts the record best representing
the best offer received and in the first branch clause receives offers and updates the best record. In
the second clause, it spawns an asynchronous wait for one second, prints the best offer, and uses a
special termination message with command type eof that is intercepted by the rcvMult reaction that
is then terminated.

This example is somewhat contrived as there are better ways to achieve the same result using
reaction groups. Here is a solution from hohpe_using_groups.prova. The sorted accumulator collects
all offers sorted by the offer values. If the @size(1) annotation was not added to the exit reaction
@or, the outputs would have continued every second. The @size(1) output just one collection with
the results and the exit reaction extracts that first result and prints the highest offer.

client() :
sendMsg(XID,self,0,request,[19]),
branch(XID).

branch(XID) :
Acc = ws.prova.eventing.SortedAccumulator(),
@group(bids) @timer('1 sec','1 sec',Acc)
rcvMsg(XID,self,Me,respond,[Service,Offer]),
Acc.processAt(Offer,Service),
println(["Received offer: ",Offer," from ",Service]).
branch(XID) :

Prova rule language 3.0

User’s Guide

May 2010

42

@or(bids) @size(1)
rcvMsg(XID,self,Self,or,[Results]),
Acc=Results.get(0),
Top = Acc.highest(1),
println(["Received best offer: ",Top]).

read_enum/2 (i,io) ND

This non‐deterministic predicate enumerates the lines returned by reading the input
BufferedReader. It is structured as a multi‐valued Prova function (see Functional programming
extensions). In the first argument, it accepts a Java BufferedReader. The predicate produces
independent solutions, one each for each line read. This creates a choice point with as many non‐
deterministic branches as there are lines in the input.
The following example shows how this built‐in predicate works.

:solve(test_read_enum_1(Line)).

test_read_enum_1(Line) :
fopen($File,Reader),
read_enum(Reader,Line).

This returns:

Line=:solve(test_read_enum_1(Line)).
Line=
Line=test_read_enum_1(Line) :
Line= fopen($File,Reader),
Line= read_enum(Reader,Line).

retract/1 (i)

This builtin predicate, standard to Prolog‐like languages, is the dual of assert/1. It takes a Prova
list (which is the same as a compound term in Prova) and unifies it with the facts in the current
rulebase until the first match is found. It then removes the matching fact or fails, if it cannot find it.
The supplied term can contain variables and unification between these variables and the constants
or variables in the fact is done using the usual unification rules. Note that Prova facts are rules
without a body and as such can contain free (uninstantiated) variables.
The following fragment will non‐deterministically enumerate the symmetric(g) and symmetric(h)
facts in the literal symmetric(X).

assert(symmetric(f)),
assert(symmetric(g)),
assert(symmetric(h)),
retract(symmetric(f)),
symmetric(X),

retractall/1 (i)

This builtin predicate, standard to Prolog‐like languages, is an extended version of retract/1. It
takes a Prova list (which is the same as a compound term in Prova) and removes all facts in the
current rulebase that are found using unification to be the same or a specialization of the supplied
term.

Consider this example.

:solve(test008(X,Y)).

test008(X,Y) :
assert(dual(h,a)),
assert(dual(h,_)),
asserta(dual(h,f)),

Prova rule language 3.0

User’s Guide

May 2010

43

retractall(dual(_,a)),
dual(X,Y),
println(["Rule B: dual",X,Y]," ").
It prints:
Rule B: dual h f
X=h, Y=f
Rule B: dual h <46>
X=h, Y=<46>

As we see, retractall(dual(_,a)) removes only the fact dual(h,a) but not dual(h,_).

sendMsg/5 (io,i,i,i,i)

This built‐in is used for sending the specified message immediately (contrast this with
sendMsgSync). The predicate takes the following arguments.
1. XID ‐ conversation id of the message;
2. Protocol ‐ name of the message passing protocol;
3. Destination ‐ a logical endpoint;
4. Performative ‐ the message type broadly characterising the meaning of the message;
5. Payload ‐ a Prova list containing the actual content of the message.
The following code responds to an inbound init message by sending two request messages to the
logical endpoint "Agent002" via the ESB protocol.

rcvMsg(XID,Protocol,From,init,[]) :
agent001().

card('Mastercard').
card('Visa').

agent001() :
card(Card),
sendMsg(XID,esb,"Agent002",request,card(Card)).

sendMsgSync/5 (io,i,i,i,i)

This built‐in is used for sending the specified message after the current goal has been fully
processed. The predicate takes the same arguments as sendMsg.
1. XID ‐ conversation id of the message;
2. Protocol ‐ name of the message passing protocol;
3. Destination ‐ a logical endpoint;
4. Performative ‐ the message type broadly characterising the meaning of the message;
5. Payload ‐ a Prova list containing the actual content of the message.
The sendMsgSync predicate is different from sendMsg that sends the message immediately to
the specified destination. Consider the following code:

switch_thread() :
% INCORRECT: must use sendMsgSync
sendMsg(XID,task,0,switch,[]),
rcvMsg(XID,task,From,switch,[]).
client() :
% Send all the test messages from a separate thread
switch_thread(),

% Use userid as conversationid (XID) for partitioning so that each user is processed sequentially
sendMsg(user1,async,0,request,login(user1,'10.10.10.10')),

The idea here is to "jump threads" so that message sending occurs on an automatically selected
thread from the task thread pool (see Concurrent reactive messaging). What actually happens is that
once the switch message is sent, the following rcvMsg inside switch_thread makes sure there is an

Prova rule language 3.0

User’s Guide

May 2010

44

inline reaction waiting for a reply on the task protocol. So while rcvMsg itself is executed on the
main thread, the reaction it creates should be ready to intercept the expected message on another
task thread. We have a race condition, the reaction may not be ready before the message arrives.
The built‐in sendMsgSync ensures that the current rule runs exhaustively until all solutions or
failure is encountered, which means, among other things, that all subsequent rcvMsg are all
executed, before the message is actually sent. So the correct code will be as above but with
sendMsgSync.

switch_thread() :
% CORRECT: using sendMsgSync
sendMsgSync(XID,task,0,switch,[]),
rcvMsg(XID,task,From,switch,[]).

This message sending primitive must typically be used whenever there are send+receive pairs in
the code and there is a possibility that the execution thread will be changed. The protocol async is
specially designed for maintaining coherence through the whole message exchange in a
conversation, in which case, a conversation is always mapped to one thread. The same is true for the
default self protocol that runs everything in a single thread.

spawn/4 (io,i,i,i)

This built‐in is used for running a Java method in a separate thread. It asynchronously returns
the results in a separate message sent on the self protocol, i.e., received on the agent's main thread.
The response is correlated using the conversation‐id that is instantiated by the predicate if a free
variable is supplied in the first argument, or on the specified conversation‐id, if it is already set.
Otherwise, the predicate accepts the following parameters.
1. XID ‐ conversation id of the message with results;
2. Target ‐ a Java object instance or a qualified Java class name;
3. Method ‐ the method to be called;
4. Args ‐ a Prova list with parameters to the call.
spawn(XID1,Object,Method,Args),
rcvMsg(XID1,Protocol,Me,return,Ret)

tokenize_enum/2 ([i,i],io) ND

This non‐deterministic predicate enumerates delimited tokens in the input string. It is structured
as a multi‐valued Prova function (see Functional programming extensions). In the first argument, it
accepts a Prova list with two input parameters: an input string and a delimiter string. The predicate
produces independent solutions, one each for each possible token in the second argument. This
creates a choice point with as many non‐deterministic branches as there are tokens in the input
stream.

The following example shows

:solve(test_tokenize_enum_1(Value1)).

:solve(test_tokenize_enum_2(Value2)).

test_tokenize_enum_1(Value) :
% nondeterministically enumerate delimited tokens in a string
tokenize_enum(["j\t12s\ttt","\t"],Value).

test_tokenize_enum_2(tt) :
% nondeterministically enumerate delimited tokens in a string
tokenize_enum(["j\t12s\ttt","\t"],tt).

This returns:

Prova rule language 3.0

User’s Guide

May 2010

45

Value1=j
Value1=12s
Value1=tt
Value2=tt

tokenize_list/2 ([i,i],io)

This predicate tokenizes an input string using a supplied separator and returns a Prova list with
tokens. The predicate is structured as a Prova function (see Functional programming extensions). In
the first argument, it accepts a Prova list with two input parameters: an input string and a separator.
The second argument is the produced list of tokens. If this list is supplied as input, it is unified
against the output list tokens.
The following example demonstrates.

:solve(test_tokenize_list("a,b,c",L)).

test_tokenize_list(In,[T|Ts]) :
% Create a list of tokens separated by ","
tokenize_list([In,","],[T|Ts]).
test_tokenize_list(In,L) :
% Create a list of tokens separated by ","
tokenize_list([In,","],L).
test_tokenize_list(In,ttt) :
tokenize_list(In,",",ttt).

This test prints the following solutions.

L=[a,b,c]
L=[a,b,c]

type/2 (i,io)

This predicate returns in the second argument the name of the Java class of the object supplied
as input in the first argument. The output may already be set to a String value, which will then return
success if the class name of the supplied object matches that string.
The following fragment will print the name of the ArrayList class..

List=java.util.ArrayList(),
type(List,TypeList),
println([TypeList]),

This prints

java.util.ArrayList

unescape/2 (i,o)

This predicate takes a string with encoded escape characters and returns a string with the actual
embedded control characters.

:solve(p(Esc1)).

:solve(q(Esc1)).

% One solution
p(Esc1) :
unescape("line1\nline2\nline3",Esc1).

% One solution

Prova rule language 3.0

User’s Guide

May 2010

46

q(Esc1) :
unescape("line1\tline2\tline3",Esc1).
The above code prints.

Esc1=line1
line2
line3
Esc1=line1 line2 line3

unique_id/1 (o)

This built‐in predicate returns a simple String that combines the logical agent name with the
sequential Long value representing a unique occurrence inside the agent instance. It can be used for
a variety of purposes, including generating unique partition ID's used by the partitioned reaction
predicate rcvMsgP.

unique_id(PID1)

This would assign PID1 to something like:

myagent:12345

Prova rule language 3.0

User’s Guide

May 2010

47

You're Reading a Free Preview

Download
scribd
/*********** DO NOT ALTER ANYTHING BELOW THIS LINE ! ************/ var s_code=s.t();if(s_code)document.write(s_code)//-->