The BeBOP System Abstract

The BeBOP System
Jason Lee 1
and
Andrew Davison
Department of Computer Science
University of Melbourne
Parkville, Victoria 3052, Australia
Email: fjasonl, [email protected]
August 1993
Abstract
The BeBOP language combines the features of parallel logic programming and object oriented
programming, with the aim of supporting the programming of multi-agent systems. An agent
is an object with its own local state and behaviour (which can alter over time), and message
passing capabilities based on object IDs. The language is supported by bp, an interactive
interpreter, which allows objects to be compiled, loaded and executed incrementally, and for
the communication links within an object network to be specied dynamically. bp is a superset
of NU-Prolog's interpreter np and so can seemlessly combine objects and Prolog goals.
Key words: parallelism, logic programming, object oriented programming, interpreter
1
Supported by a grant from The Australian Research Council
1 Introduction
with others. A very useful by-product of bp's environment is the avoidance of oundering of ordinary
The main capabilities of the type of system we Prolog queries.
are interested in modeling were described by Hewitt [Hew85]:
Continuous change and evolution. There
is no such thing as static information, knowlThis section describes some of the BeBOP lanedge, or system structure.
guage's important features. Succinctly, BeBOP
Arms-length relationships and decen- is a parallel LP language with extra notation to
tralized decision making. There are no facilitate object oriented programming. Complex
global objects, only local states and control. knowledge can be coded as Prolog meta-level theAgents must communicate since they do not ories, but this aspect of the language will not be
have access to one another's memory.
described (see [Dav93b, LD93]).
2 Language Highlights
Multiple perspectives, with perpetual
inconsistency among knowledge bases.
Because of the previous points it is impossi- 2.1 Logic Programming
ble to maintain consistent knowledge.
BeBOP objects can use NU-Prolog and its parallel
extension, PNU-Prolog, to carry out their actions.
NU-Prolog is a coroutining Prolog, which allows the
denition of when declarations to specify delaying
conditions for user-dened predicates [TZ90].
PNU-Prolog is a subset of NU-Prolog (with some
extra syntax) which supports don't know nondeterminism and stream AND-parallelism [Nai87].
For example, the following stack `object' accepts
push and pop messages, and succeeds at the end of
the input stream when the stack is empty:
Need for negotiation among system components. Agents must negotiate for resources, for tasks, and for information when
there is no centralized control.
Inadequacy of the closed-world assumption. There is no such thing as a real closed
world.
These attributes suggest that an agent, or object, requires both object oriented features (encapsulation, message passing) and Logic Programming
(LP) capabilities (inference, relationships between
entities, unication). The utility of unication is
often overlooked { it combines the functionality of
assignment, equality testing, data access, data construction, and dierent forms of parameter passing.
In a concurrent setting, it is invaluable as a highly
expressive communication mechanism.
In the following sections, the language BeBOP is
introduced { it supports multi-agent systems of the
type described above. In particular, it encourages
the simple description, decomposition and distribution of tasks among objects and their interaction by means of a combination of object oriented
and concurrent LP functionality. A simple producer/consumer is described, and the implementation of the language is summarized.
An important part of the BeBOP system is the
interactive interpreter environment, bp. It allows
classes to be incrementally compiled and loaded,
and also permits the incremental invocation of an
object network. In particular, it is possible to invoke an object and sent it messages at any time
during the session, and dynamically link the object
?- lazyDet stack(i, i).
% `i' mean input argument
% `o' means output
stack([push(X)|Ss], Stk) :stack(Ss, [X|Stk]).
stack([pop(X)|Ss], [X|Stk]) :stack(Ss, Stk).
stack([], []).
The lazyDet declaration species that a stack/2
goal will delay until all of its clauses are suciently
instantiated so that no input arguments are bound
in the head, or before a cut. Such predicates should
be deterministic, and the system will execute them
in stream AND-parallel with other lazyDet goals,
assuming that backtracking between those goals
cannot occur.
There is also an eagerDet declaration which
species that a goal should delay until one of its
clauses is suciently instantiated to avoid binding input variables in the head, or before a cut.
eagerDet declarations are useful for predicates
which use don't care non-determinism.
1
2.2 Object Oriented Programming
predicates. Unfortunately, for implementation reasons, their values can only be changed by initial
goals and clauses, see [LD93] for more details.
Visible variables are seen by the caller of the
class; they can be assigned the usual data structures (terms, variables), and IDs of other objects.
An ID is a constant which has two main roles:
it uniquely identies its object and, more importantly, is a communication mechanism for sending
messages to the object. IDs can also be passed to
other objects inside messages, so allowing communication links between objects to vary at run time.
The invisible variables declaration begins with
the keyword invisible, and species variables
that are local to the class.
Visible and invisible variables are typed as either
input (i) or output (o). Invisible variables can be
assigned a value at object invocation time.
For example, the `foo' class below has two visible
variables: V1 and V2. V1 is an input variable, V2 is
output. `foo' also has two invisible variables: IV1
and IV2, both of which are input variables. IV2 is
assigned 0 when the object is called.
Each BeBOP object is assigned a unique ID which
can be used by other agents for communication.
This is unusual for a LP language, which would
typically use shared variables for communication.
The main reasons for the change were to introduce
a clear notion of object identity into BeBOP, and to
make inter-object communication simpler to specify.
Importantly, an object only `knows' about other
objects if it is given their IDs { a situation
somewhat similar to acquaintances in Actor languages [Agh86].
A notable object oriented feature missing from
BeBOP is inheritance. Experience of supporting
inheritance in object oriented concurrent LP languages (c.f. Polka [Dav89]) shows that inheritance
is dicult to use when an object exhibits concurrency. However, much of the functionality of inheritance can be obtained by using a mixture of delegation (message forwarding between objects) and
shared data.
3 Language Description
foo
V1 i, V2 o
invisible IV1 i, IV2 i <= 0
clauses
:
end.
The BeBOP class structure is summarized by the
following informal BNF:
When an object is invoked, it is assigned a unique
ID { this can be referred to inside the object using
the variable Self. This allows an object to send
itself messages, in the style of the self communication in languages like Smalltalk. Also, the object
outputs the ID as part of its invocation, so enabling
the calling object, predicate, or user to utilize the
ID.
Messages sent to an object ID are eventually
added to the end of an input stream for the object. This stream is available to the object through
the default invisible variable called Input, which
represents it as a partially instantiated list. Each
term on the list is a message, and the list ends in a
variable which can be bound to further messages.
The initial section contains goals which will
be carried out after the object has been invoked,
but prior to the processing of any input messages.
Typically, the goals of an initial section initialize invisible variables in more complex ways and
output initial messages.
The clauses section contains a sequence of
clauses, separated by `.'s. A clause has the form:
<class name> [<visible variables>]
[invisible <invisible variables>]
[initial <actions>]
clauses
% the clauses section
<clauses>
[pnu <PNU-Prolog predicates>]
[nu <NU-Prolog predicates>]
end.
The keywords are invisible, initial, clauses,
pnu, nu and end; the phrases in angled brackets
are informal descriptions of the text that should be
placed there; square brackets around text denote
that it is optional.
The two groups of variable declarations after the
class name share the property that they specify
variables which can be used in the initial goals
of the class, and in its clauses. They can also be
accessed directly by predicates in the pnu and nu
sections, without passing them as arguments to the
<input message pattern>
2
=>
<action>
<action>
includes the following forms :
ID1 :: hello(Ack)
This sends the message hello(Ack) to the object
whose ID is stored in ID1. It is possible for the
receiver to bind Ack and so acknowledge the sender.
<guard> : <goals>
if <guard> then <goals> else <goals>
A clause is executed if its input message pattern
matches with the message at the head of the input
stream of the object, and the guard of the clause
succeeds. A guard is an optional conjunction of
goals which can carry out more complex tests before the clause is chosen. Guard goals should not
bind variables in the incoming message (or in the
class variables); bindings can only be carried out by
the goals on the right of the commit symbol (the
`:') after the clause has been selected. This restriction allows the clause to be translated into a clause
of a lazyDet predicate at compile time. This, in
turn, allows the object to execute in stream ANDparallel with other objects.
If pattern matching or the guard fails, execution
can backtrack, and another clause can be tried.
However, once a clause has been selected, and execution has passed beyond the guard, backtracking
is no longer permitted. The clause has committed.
After commitment, the message is deleted from
the head of the input stream, and the clause goals
are executed. Once these are completed, the object will select a clause which can process the next
message on the input stream, and so execution progresses. If there is no message available, then the
object will suspend until one arrives.
A clause without a guard must still have a commit operator before any of its goals.
If none of the clauses are suitable, it is possible
for the object to fail. Thus, it is advisable for the
programmer to include a `catch-all' clause which
signals an error and continues.
The pnu and nu sections hold the denitions of
augmented PNU-Prolog and NU-Prolog predicates
respectively. The predicates are augmented in the
sense that they can contain calls to the BeBOP `::'
operation for sending messages. The contents of
these sections are local to the class, thereby enforcing information hiding.
The initial and clauses sections can call pnu
and nu section goals, and also three special operations: `::', becomes, and i am.
3.2 The becomes Operation
The syntax of this statement is:
<visible/invisible variable>
becomes <term>
e.g.
List becomes [element(El)|List]
does not carry out destructive assignment,
but something more akin to value replacement. It
does this after the other goals of the clause (or
initial section) have been executed. Thus, the
textual position of a becomes in a clause is irrelevant and, in the example above, the programmer
should not attempt to use the new List value since
List will retain its current value until the end of
the clause. However, the value will be updated before the next incoming message is processed.
A useful variant is the multiple becomes :
becomes
[A, B] becomes [NewA, NewB]
which updates A and B. Also the right hand side
of a becomes may be an arithmetic expression :
A becomes A + 1
The unusual semantics for becomes are to avoid
the introduction of destructive assignment into the
language.
3.3 The i am Operation
The syntax is:
i_am class <goal>
or
i_am <goal>
An i am goal causes the object to call the named
goal and then ceases execution. As with the
becomes the textual position of the i am is irrelevant. This functionality is useful in two main situations:
1. When the object should change into another
object (c.f. the Actor languages [Agh86]).
2. When the object wishes to terminate, by replacing itself with the goal true. Naturally,
a more complicated termination would require
the calling of a more complicated goal.
3.1 The `::' Operation
The syntax of this statement is:
<Object ID> :: <message>
e.g.
3
3.4 The call class/1 Predicate
close_down => : i_am true.
The syntax is :
pnu
?- lazyDet generate(o, o, o, o).
generate(RNum, A1, B1, C1) :Xa1 is A0 * 171, A1 is Xa1 mod 30269,
Xb1 is B0 * 172, B1 is Xb1 mod 30307,
Xc1 is C0 * 170, C1 is Xc1 mod 30323,
Xa2 is A1/30269.0, Xb2 is B1/30307.0,
Xc2 is C1 / 30323.0, Xt1 is Xa2 + Xb2,
Xt2 is Xt1 + Xc2, Xt3 is integer(Xt2),
RNum is Xt2 - Xt3.
call_class(<class>).
The call class predicate causes the object to call
the named class and continue execution (see the
producer class in section 4.2).
4 Producer and Consumer
Example
A consumer object requests integers from a pro- end % end of class
ducer object, which generates them using a random
number generator object. The consumer stores the
numbers that it receives, and prints out a tally 4.2 producer
When a producer class is invoked, it internally inwhen it has received a hundred.
vokes a random number generator object in its initial section. This object will only be accessible to
4.1 rand
the producer (unless the producer chooses to pass
The random number generator class rand can ac- its ID to another object).
cept the following types of message :
A producer object accepts messages of the following
type :
rand(Lower, Upper, Random)
prod(Value)
and
and
close_down
close_down
and
specify the range ([Lower,
)) of the random number Random.
message causes the object to terA
minate.
The seeds for the random number are invisible
variables and so local to the object, and are initialized when the object is invoked. A new random
number is generated using generate/4 which accesses the seed variables A0, B0, C0 directly without
needing to be passed their values. New seed values
are returned and become the new values for A0, B0,
C0.
Lower
Upper
Upper
close down
A prod message requests a binding for Value,
which is generated by the producer by sending a
rand message to its random object; this fact is hidden from producer's users. A close down message
causes the object to terminate, along with its random number generator.
producer
invisible RandId i
% Rand class ID
initial :
call_class(rand(ID)),
writeln('Started up producer'),
RandId becomes ID.
% Save it's ID
rand
invisible A0 i <= 27134, B0 i <= 9213,
C0 i <= 17773
clauses
prod(Value) => :
RandId :: rand(0, 5, RNum),
Value = RNum.
clauses
rand(L, U, RNum) =>
integer(L), integer(U) :
generate(R1, A1, B1, C1),
[A0, B0, C0] becomes [A1, B1, C1],
Xt1 is U - L, Xt2 is Xt1 * R1,
Xt3 is integer(Xt2), RNum is L + Xt3.
close_down => : RandId :: close_down,
i_am true.
end
4
% end of class
4.3
5 bp - BeBOP interpreter
consumer
The bp interpreter is an interactive system which
can be viewed as an augmentation of np, the NUProlog interpreter [TZ90]. Within bp, the user can
compile and invoke BeBOP classes or Prolog predicates.
A
object is supplied with the ID of a
when it is invoked. It also initializes
, which will record how many integers the
object has received, and NumLt, which will record
the number of occurrences of each number.
The object invokes its clauses section by sending
a get message to itself. This causes the object to
either terminate (if Counter is greater than 100)
or to request an integer from the producer. Upon
termination, the occurrence list (NumLt) is printed
out.
In the else clause, a standard list manipulation
predicate, add list/3, updates NumLt. Also another get message is sent to Self in order to continue the consumption of integers.
consumer
producer
Counter
5.1 Compilation and Execution
The interpreter is invoked at the UNIX prompt :
bp
>
After a welcome message, a prompt appears :
1 -->
The user is ready to compile and load BeBOP class
les :
bebop(producer), [producer].
1 -->
consumer PID i
bebop/1 compiles the le producer.bp, and the
square brackets notation loads the result.
An alternative method is to supply the le name
on the UNIX command line :
% The producers ID
invisible Counter i <= 0,
NumLt i <= []
bp producer
This invokes bp, and compiles and loads the le
>
initial : writeln('Starting consumer'),
Self :: get.
before prompting for a query.
Yet another method is to compile the le using
the bebop compiler and load the compiled le into
bp at a later stage :
>bebop producer % bebop compiler
clauses
get => if (Counter > 100) then
print_list(NumLt),
i_am true
else
PID :: prod(C),
add_list(NumLt, C, NewNumLt),
[Counter, NumLt] becomes
[Counter + 1, NewNumLt],
Self :: get.
bp
>
1 -->
[producer].
After the le is loaded, the system prompts :
2 -->
to which the user can invoke a producer object and
send it a prod.
pnu
?- lazyDet print_list(i).
print_list([]).
print_list([n(No, Occur) | Rest]) :write('Number '),write(No),
write(' occurred '),write(Occur),
writeln(' times.'),print_list(Rest).
producer(ID), ID::prod(C).
2 -->
5.2 Incremental Message Passing
Each variable used in a query is renamed with a
new sux { a `$' followed by the history number.
They can be used in later queries to send messages
to objects which were invoked earlier.
In the example below, a producer object is sent
two prod messages, the second sometime after the
object was invoked. Note the use of the l command
to list the variable renamings.
% keep a count of the
% number and its occurrences
?- lazyDet add_list(i, i, o).
add_list([], C, [n(C, 1)]).
:
end
% end of class
5
producer(ID), ID::prod(C).
1 -->
C=3
ID = 1
:
l.
4 -->
1
a certain consumer, but this is changed when the
consumer terminates, and a second is initialized
%bindings
> bp consumer producer rand
% compilation messages given
%list renamings
ID -> ID$1 C -> C$1
:
1 --> producer(PID).
PID = 1
2 --> l.
1
PID -> PID$1
ID$1::prod(C).
5 -->
C=2
ID$1 = 1
% Start producer
6 -->
2 --> consumer(_, PID$1).
% Start up consumer with producer's ID
Starting consumer
Number 0 occurred 14 times.
Number 1 occurred 26 times.
Number 2 occurred 21 times.
Number 3 occurred 17 times.
Number 4 occurred 22 times.
5.3 Delayed Invocation
The following example shows how an append/3 goal
can suspend and be woken later by binding its variables. This sort of interaction is not possible in the
np system which simple reports the append/3 goal
as having oundered.
1 --> append(K, L, M).
2 --> l.
1
K -> K$1
L -> L$1
PID$1 = 1
Again ?
%return typed and
%consumer finishes
3 -->consumer(_, PID$1).
% Start up another consumer, using
% the same producer
Starting consumer
Number 0 occurred 15 times.
Number 1 occurred 18 times.
Number 2 occurred 22 times.
Number 3 occurred 17 times.
Number 4 occurred 28 times.
Again ?
%return typed and
%consumer finishes
M -> M$1
2 --> M$1 = [1,2].
% Unify M$1, M in delayed append/3 query
M = [1, 2]
L = [1, 2]
K = []
M$1 = [1, 2]
Again ? ;
M = [1, 2]
L = [2]
K = [1]
M$1 = [1, 2]
Again ?
%return typed
3 -->
The producer will suspend again after the second
consumer terminates, but can be terminated by the
user.
4 --> PID$1::close_down.
5.4 Using the producer/consumer
6 Implementation
This example shows the producer/consumer classes
being compiled, as part of the invocation of bp.
Initially only a producer object is invoked, the consumer being started later. The two are linked together by supplying the consumer with the ID of
the producer. Once the rst consumer has nished
the producer will delay until a request comes from
the second consumer.
This example shows, albeit it in a simple form,
the way that an object network can be recongured
at run time. Initially, the producer is `linked' to
A BeBOP class is translated into a series of PNUProlog and NU-Prolog predicates, one predicate for
the clauses section and one for each predicate of
the pnu and nu sections.
Each clause of the clauses section is translated
into a clause of a PNU-Prolog predicate. This
means that the semantics of PNU-Prolog for choosing a clause can be used to explain the semantics of
selecting a BeBOP clause. The name of the translated clause is the name of class prexed with `c ',
6
and its arguments are the visible and invisible variables of the class. There are also three extra variables, called Input, BB$ Mesg and Self which are
explained below. The body is made up of the goals
of the clause (with some modications), and a recursive call to the `c' predicate. For example, the
clause in the class:
foo
invisible Num i
clauses
incr(N) => : Num becomes Num + N.
:
end
is rewritten to:
foo
invisible Num i
:
clauses
prod(Val) => :
Val = Num.
:
end
c_foo([incr(N) | Input], Self,
Num, BB$_Mesg) :!, Temp$bb_1 is Num + N,
c_foo(Input,Self,Temp$bb_1,BB$_Mesg).
Temp$bb 1 is a new variable which is guaranteed to
be unique in the clause.
becomes:
6.2 The i am Operation
?- lazyDet c_foo(i, i, i, i).
c_foo([prod(Val) | Input], Self,
Num, BB$_Mesg) :!, Val = Num,
c_foo(Input, Self, Num, BB$_Mesg).
i am goals become new body goals and terminate
the object by disabling the recursive call to the `c'
predicate. For instance, in `foo':
The rst argument of c foo/4 is for the Input variable (the input stream to the object). At run time,
a message is read o the stream by pattern matching, and the remaining part is passed to the recursive call of c foo/4 so that further messages can
be received. The Self variable holds the ID of the
class. The cut in the body makes the clause deterministic.
The visibility of class variables is maintained by
generating a user-level predicate with the name of
the class as its name and the visible variables (and
BB$ Mesg) as arguments. This predicate calls the
`c' predicate, as illustrated by the user-level predicate generated for the `foo' class :
terminate => :
i_am true.
is rewritten to:
c_foo([terminate | Input], Self,
Num, BB$_Mesg) :!, true.
6.3 The `::' Operation
The intended meaning of a message passing operation, such as ID1::Msg, is that Msg will be added to
the input stream of the object whose ID is stored
in ID1. This is made possible by having all objects
share a single variable, BB$ Mesg. For instance,
consider the user-level invocation of the two objects
producer, and consumer :
?- lazyDet foo(i, o).
foo(BB$_Mesg, Self) :$bb_create_id(BB$_Mesg, Self, Input),
c_foo(Input, Self, Num, BB$_Mesg).
--> producer(PID), consumer(CID, PID).
PID, and CID are the IDs of the respective objects.
The second argument of foo/2 is the unique ID for Note
that in this example consumer has access to
the object which is created and linked to Input by producer
's ID.
$bb create id/3. The details of this predicate are
The
translation
of the query to PNU-Prolog will
explained below.
introduce
the
shared
variable BB$ Mesg.
Any initial section goals are added to the body
of the user-level predicate before the call to the `c' ?- producer(BB$_Mesg, PID),
predicate.
consumer(BB$_Mesg, CID, PID, 5).
BB$ Mesg acts as a partially instantiated list, which
6.1 The becomes Operation
holds terms of the form:
id(ID, Input)
becomes calls are converted into changes to the arguments of the recursive call of the `c' predicate. ID is the ID created for an object, and Input is its
associated input stream.
For instance, in `foo':
7
6.4 The pnu and nu sections
At object invocation time, $bb create id/3
nds the end of the BB$ Mesg list and adds a new
`id/2' term to it. Note, that the list must always
end in a variable, in order to permit further IDs
to be added. This means that $bb create id/3
must be meta-logical because of the need to detect the list's uninstantiated tail. The cost of a
$bb create id/3 call is O(N), where N is the number of invoked objects.
We are now in a position to explain how `::' is
implemented. Consider a simple clause in the consumer class which sends a message to the producer
class and to itself :
All predicates in the pnu and nu sections are given
new names by prexing the old names with the
class name. Also their arities increase to include
all visible and invisible variables. For instance;
print list/1 in the consumer class (section 4.3)
becomes :
?- lazyDet
consumer_print_list(i,i,i,i,i,i,i).
consumer_print_list([], Input, Self,
PID, NumLt, Counter, BB$_Mesg).
consumer PID i
:
clauses
get => PID :: prod(C),
Self :: get.
:
end
consumer_print_list([n(No, Occur) | Rest],
Input, Self, PID, NumLt,
Counter, BB$_Mesg) :write('Number '), write(No),
write(' occurred '), write(Occur),
writeln(' times.'),
consumer_print_list(Rest,...,BB$_Mesg).
The clause is rewritten as:
6.5 The call class/1 Predicate
c_consumer([get | Input], Self, PID,
BB$_Mesg) :!, $bb_send(BB$_Mesg,PID,prod(C),[]),
$bb_send(BB$_Mesg, Self, get,[]),
c_consumer(Input,Self,PID,BB$_Mesg).
The BeBOP compiler increases the arity of the
predicate to include the shared message variable, BB$ Mesg.
The arity of the class argument of call class is
calculated, and this together with the class name
is used to determine whether the compiled code for
the class has been loaded into bp. If the code is
present, then a BB$ Mesg argument is added to the
arguments of the class, which is then invoked. If
the code is not present an error is reported.
call class
$bb send/4 searches along BB$ Mesg to nd the
`id/2' term holding PID, and then it nds the uninstantiated tail of the corresponding stream, in order to add the message to it. This approach means
that $bb send/4 is O(N*M), where M is the number of messages sent to the specied object since
its invocation.
Optimizations are possible, one being to store
the new end of the input stream after a message
has been sent. This would reduce the cost of
$bb send/4 to O(N), but would require it to return a new BB$ Mesg variable. This, in turn, would
require the clause translation to use the newest version of BB$ Mesg in each call to $bb send/4.
Another, simpler, optimization is to replace the
partially instantiated lists by binary trees, making
the overheads logarithmic rather than linear.
A complication is that predicates in the code section can also use `::'. This requires that an analysis
of those predicates be carried out, in order to determine which should be given an extra argument
for the BB$ Mesg variable.
The fourth argument of $bb send/4 is for a list of
suspendible variables, a feature described in detail
in [Dav93a].
6.6 bp
bp augments np with goal suspension to aid
coroutining, and the notation of `global' variables. The implementation of bp centers on
the $bebop loop/4 predicate which prints out a
prompt and reads in the users queries, checking for
any syntax errors. This predicate also records certain state information during a session : the current
history number, a history list, a list of mappings between the old and new names associated with variables used in queries, and the global shared message variable BB$ Mesg. The predicate recursively
calls itself making the state information available
to each new query.
Each query entered by the user is rst examined
by the $bebop rename/8 predicate in case it is a
top level goal, (eg. h to list out the history). Otherwise the query is parsed and possibly renamed or
8
References
augmented with an extra variable. For instance,
calls to class predicates are changed to include the
shared message variable BB$ Mesg, while system
predicates, are left unchanged. If the goal is a message send operation, it is transformed into a call to
the $bb send/4 predicate.
When parsing and renaming is completed, the
new query is examined for any references to global
variables. The predicate $varmapping/7 converts
variables of the form X$i, to the actual variables
used in the past query (the X variable in the 1st
query in this case). All other variables used in the
query are renamed and recorded in the list of mappings held by $bebop loop/4.
The nal outcome is a new query with all class
calls augmented with the BB$ Mesg variable, all
uses of the `::' operator changed to calls to the
$bb send/4 predicate, all global variable references
resolved, and any other variables stored for future
reference.
This altered query is executed, and the interpreter continues, independent of whether the query
delays or not. When the query nishes, any bindings are displayed to the user.
[Agh86] G Agha. Actors: A Model of Concurrent
Computation in Distributed Systems. MIT
Press, Cambridge MA, 1986.
[Dav89] Andrew Davison. Polka: A Parlog Object Oriented Language. PhD thesis, Dept.
of Computing, Imperial College, London,
September 1989.
[Dav93a] Andrew Davison. Separating Synchronisation and Functionality in Concurrent Logical Objects. Technical Report 93/18, Dept
of Computer Science, The University of Melbourne, 1993.
[Dav93b] Andrew Davison. The Deductive and Object
Oriented features of BeBOP. Technical Report 93/6, Dept of Computer Science, The
University of Melbourne, 1993.
[Hew85] C E Hewitt. The Challenge of Open Systems.
Byte, 10(4):223{242, April 1985.
[LD93] Jason Lee and Andrew Davison. BeBOP Reference Manual. Dept of Computer Science,
The University of Melbourne, August 1993.
[Nai87] Lee Naish. Parallelizing NU-Prolog. Technical Report 87/17, Dept of Computer Science,
The University of Melbourne, 1987.
[TZ90] James A. Thom and Justin Zobel. NU-Prolog
Reference Manual, Version 1.5.24. TechniThe BeBOP language oers a notation suitable for
cal Report 86/10, Dept of Computer Science,
coding multi-agent (multi-object) systems based on
The University of Melbourne, 1990.
7 Summary
a combination of parallel logic programming and
object oriented programming.
BeBOP supports information hiding, operations
for state and behaviour change, and message passing based on object IDs. These features allow the
encoding of networks of parallel objects with local states and control, evolving behaviour, and the
ability to interact in a dynamic manner { the attributes required by Hewitt's agent model.
The bp interpreter supports BeBOP programming by allowing the exible invocation of objects,
and the means for setting up communication links
between objects at any time. An incidental benet
is the ability to use `global' variables in any queries.
Since bp is an augmentation of the np system, objects and Prolog goals can be combined, and use
can be made of np's built-in libraries and tracing
features. However, an object-based tracer would be
more useful, and its implementation is in progress.
Obtaining the BeBOP system
The packaged BeBOP source, called bebop.tar.Z,
which includes the pnp preprocessor, can be found at
the anonymous ftp site munnari.oz.au (128.250.1.21),
in the pub directory. A new departmental ftp site called
ftp.cs.mu.oz.au is planned.
9