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
© Copyright 2026 Paperzz