Like notifications, subscriptions are newline terminated strings that parse as compound Pedro terms. The functor of each subscription term is always subscribe
and has arity 3. In other words, each subscription term is of the form subscribe(
Head,
Body,
Rock)
. Following the similarity between subscriptions and Prolog clauses we refer to the first argument as the head of the subscription and the second argument as the body of the subscription.
The third argument is commonly referred to as a rock. This is an integer and its meaning is determined by each client. When the Pedro server matches a notification against the subscription, the server sends the notification string together with the rock to the subscribing client. A given client can use the rock to, for example, refer to a message queue or a thread and thereby determine how to process the notification.
The Pedro server will match a notification against a subscription if the notification term unifies with the head of the subscription and, with this unifier, the goal in the body of the subscription is satisfied. Readers are referred to Prolog references for descriptions of unification, variable binding, dereferencing, occurs check, backtracking and goal evaluation.
When the server matches a notification against a subscription it will send the string consisting of the subscribers rock followed by a space followed by the notification string (including the newline).
The server will acknowledge an attempt by a client to subscribe with a string consisting of an integer (an ID) followed by a newline. The ID will be 0 if the subscription attempt fails (the string is too long, it doesn't parse, or does not represent a valid subscription term). If the subscription attempt succeeds then the ID will be a unique (for that client) positive integer. This ID is used when the client chooses to unsubscribe.
The following table lists the “basic” valid subscription goals and their semantics that can be used in the body of subscriptions. As with Prolog, whenever a unification is carried out, the variable bindings implied by the unifier are applied.
Note that, unlike most Prologs, unification in Pedro uses the occurs check.
true
fail
T1 = T2
T1
and T2
unify.T1 is T2
T1
unifies with the evaluation of the the arithmetic expression T2
. The goal produces a type error if T2
does not represent an arithmetic expression that can be fully evaluated (to a number). The valid arithmetic expressions are described later.T1 < T2
T1
evaluates to a number that is less than the evaluation of the arithmetic expression T2
. The goal produces a type error if either term does not represent an arithmetic expression that can be fully evaluated (to a number).T1 =< T2
T1 > T2
T1 >= T2
member(X, L)
L
is a “cons” pair and X
unifies with the head element of L
or X
is a member of the tail of L
.split(L1, L2, L3)
L2
and L3
unifies with L1
. The list L1
must be supplied.splitstring(S1, S2, S3)
S2
and S3
unifies with S1
. The string S1
must be supplied.number(T)
T
is a number.atom(T)
T
is an atom.string(T)
T
is a string.list(T)
T
is either the empty list or a “cons” pair.The following table lists the valid meta-level subscription goals – i.e. goals that take valid goals as arguments.
G1 , G2
G1
succeeds and then the goal G2
succeeds.G1 ; G2
G1
succeed or the goal G2
succeeds.G1 -> G2 ; G3
G1
succeeds then alternative solutions for G1
are removed and the goal succeeds if and only if G2
succeeds. Otherwise, the goal succeeds if and only if G3
succeeds.not(G)
G
fails. In Prolog this form of negations is called “unsafe negation”.once(G)
G
succeeds. Alternative solutions are removed.The following are valid arithmetic expressions. Numbers are valid arithmetic expressions and in the table below, E1
and E2
are valid arithmetic expressions.
pi
e
- E1
E1 + E2
E1 - E2
E1 * E2
E1 / E2
E1 // E2
E1 ** E2
E1 rem E2
E1
and E2
are integer expressions)E1 mod E2
E1
and E2
are integer expressions)E1 /\ E2
E1
and E2
are integer expressions)E1 \/ E2
E1
and E2
are integer expressions)\E1
E1
is an integer expression)E1 << E2
E1
and E2
are integer expressions)E1 >> E2
E1
and E2
are integer expressions)abs(E1)
round(E1)
floor(E1)
ceiling(E1)
sqrt(E1)
sin(E1)
cos(E1)
tan(E1)
asin(E1)
acos(E1)
atan(E1)
log(E1)
The following are examples of valid subscriptions. In all these examples, the rock is zero but can be any integer (e.g. thread ID).
subscribe(info(fred, X), true, 0)
: in this example, the goal is true
and will always succeed. Hence, this subscription will match against any notification the unifies with the head term – i.e. any compound term whose functor is info
, has arity 2 and has first argument fred
subscribe(data(L), (member(height = H, L), H > 1000), 0)
: the head of this subscription matches against any notification with functor data
and arity 1. The subscription will match a notification if the notifications argument is a list that contains a term of the form height = H
and H
is a number greater than 1000. Note that, as with Prolog, operators can be used to build terms (even when the operator semantics is not being used). In this case the operator =
is just a convenient infix operator used to construct an arity 2 compound term.subscribe(foo(X, X), (X < 10; X > 20), 0)
: the head matches against any notification with functor foo
, has arity 2, and whose arguments are unifiable. The body succeeds if X
is either less than 10 or greater then 20.subscribe(str(S), (splitstring(S, _, S2), splitstring(S2, "hello", _)), 0)
: this subscription matches any notification with functor str
and arity 1 and whose argument is a string containing "hello"
as a substring.subscribe(foo(X, Y), (atom(X) -> number(Y), Y > 0 ; atom(Y)), 0)
: this subscription matches any notification with functor foo
, has arity 2, and if its first argument (X
) is an atom then Y
is a number greater then 0, else Y
is an atom.The following example illustrates what happens when type errors occur in an attempted match of a subscription and a notification.
Consider the subscription
subscribe(foo(X, Y), (X < 0 -> Y > 10 ; Y < 10), 0)
and the notification
foo(bar, 0)
In this case the head of the subscription matches the notification but X
is not a number and so the test X < 0
produces a type error which causes the attempted match to fail.
If the intention of the subscription was to test if X is a number less than 0 then it should be written as follows.
subscribe(foo(X, Y), (number(X), X < 0 -> Y > 10 ; Y < 10), 0)
In this case the notification above will match.