[net.lang.prolog] PROLOG Digest V1 #7

PROLOG-REQUEST@SU-SCORE.ARPA (06/03/83)

From:  Chuck Restivo (The Moderator) <PROLOG-REQUEST@SU-SCORE.ARPA>


PROLOG Digest             Friday, 3 Jun 1983        Volume 1 : Issue 7

Today's Topics:
           Representation - Declaring Predicates Transitive,
                        Applications - Objects
----------------------------------------------------------------------

Date: 30 May 1983 1543-EDT (Monday)
From: Vijay.Saraswat@CMU-CS-A
Subject: Declaring Predicates Transitive

The following solution is adequate for all the 4 cases mentioned by
Abbott:

transitive(R,Elem):-
                atom(R), nonvar(Elem),
                Rxy=..[R,X,Y], Rxz=..[R,X,Z], Rzy=..[R,Z,Y],
                assert((Rxy:- nonvar(X),Rxz, X \==Z, Rzy)),
                assert((Rxy:-nonvar(Y),Rzy, Y \==Z, Rxz)),
                assert((Rxy:- X is Elem, Rxy)).

Elem is any element in the domain over which R is defined.

-- V. Saraswat.

------------------------------

Date: Tuesday, 31 May 1983 14:12-PDT
From: Narain at Rand-Unix
Subject: Reply to Henry Liebermann

1) I agree that actors may be implemented in any language.  The
advantage of of choosing Prolog is however that you can use its most
powerful features like pattern matching, automatic backtracking,
deductive facility and well understood theoretical foundations.
Because of these features, an object oriented interpreter in Prolog
has a very simple definition.

Both styles "Object Oriented" and "natural Prolog" are very powerful
and the nice thing is to be able to use both in the same system.

2) An improvement in Kahn's "Actors in Prolog".

In Kahn's paper, actor behaviors are stored as clauses under just one
single predicate "sent", each of which has the form:

  sent(Actor,Message,Result):-<body having other calls to sent>.

To compute the result "R" of passing message "M" to actor "A", one
simply makes the call:

         :-sent(A,M,R)

Clearly, as more objects are added and more behaviors defined, any
call to "sent" will have to search through all clauses under "sent".
This effectively means that the knowledge base of all actors is being
searched, even when many actors may be totally irrelevant to the one
that received the message.

A fairly straightforward improvement over this search strategy is
given.  Essentially, the search for a matching procedure is made on
nodes only along one branch of the inheritance tree instead of on all
the nodes of the inheritance tree.  The idea is simple and no doubt
exists in other object oriented implementations.  It does at Rand
where we have developed a language called ROSS for doing
knowledge-based simulations.

When an actor receives a message, it determines its immediate ancestor
and examines its knowledge base for a matching procedure.  If it finds
a match it executes it, otherwise it goes on to the next ancestor in
the inheritance tree.

The convention adopted here is that all clauses in an <actor-name>'s
knowledge base are stored under the predicate <actor-name>_behavior.
Thus to search the knowledge base of <actor-name> we only need to
examine clauses under <actor-name>_behavior, and not all clauses under
"sent".

The modified interpreter is:

sent(Actor, Msg, Result):-
        ancestor(Actor, Actor_anc),
        procedure(Actor_anc, Actor, Msg, Result, Procedure),
        call(Procedure),
        !.

/* The "cut" is necessary only if we wish to constrain the response
to be the first one found.  It is strictly unnecessary otherwise.
When "Actor" receives "Msg", the result is "Result" if "Actor_anc"
is an ancestor of "Actor", and  "Procedure" is a call to
<Actor_anc>_behavior, and "Procedure" is successfully called.

Kahn's actor classes or generic actors are essentially "records"
and are implemented using Prolog lists.  A generic actor is just
a list of partially instantiated elements where each element
represents one field of the "record".  An instantiation of this
generic actor is just a term that matches the generic actor (a list).

Predicate "procedure" defines for each generic actor (its first
argument), the call to clauses inside its knowledge base, and binds
"Procedure" to this call. "Myself" is the actor that was originally
sent the message.

Thus:  */

procedure([list], Myself, Msg, Result, list_behavior(Myself, Msg,
Result)).  procedure([nlist,_,_,_],
Myself,Msg,Result,nlist_behavior(Myself,Msg, Result)).
procedure([slist|X],Myself,Msg,Result,slist_behavior(Myself,Msg,Result)).
procedure([],Myself,Msg,Result,null_list_behavior(Myself,Msg,Result)).

/* Definition of 'ancestor' */ ancestor(X,X). /* search begins with
the actor, so it is its own "ancestor" */ ancestor(X,Y):-parent(X,Y).
ancestor(X,Y):-parent(X,Z),ancestor(Z,Y).

/* Hierarchy in the context of lists */ parent([],[list]).
parent([nlist,_,_,_],[list]).  parent([slist|X],[list]). /* a simple
list, where "slist" stands for "cons" */

/* Now "nlist_behavior", "list_behavior" etc. are defined as they
were originally by Kahn.  Only the behaviors for the "print_elements"
message are outlined here, but other behaviors in Kahn's paper
including the Sieve of Eratosthenes alorithm were also programmed.

Note that behaviors are still defined with the same clarity, if the
naming convention is adhered to */

list_behavior(Myself,print_elements,_):-
        sent(Myself,you_are_empty,true),!.

list_behavior(Myself,print_elements,_):-
        write(' '),
        sent(Myself, first, First),
        write(First),
        sent(Myself, rest, Rest),
        sent(Rest,print_elements,_).

/* Special behavior for nlist */
nlist_behavior(Nlist,print_elements,_):-
        sent(Nlist,length,Length),
        Length > 5,
        sent(Nlist, print_elements_with_dots, _).

When an actor of the form [slist|X] receives a message to
"print_elements" it finds no behaviors in its own knowledge base.  It
then determines that it is an instance of [list] and finds an
appropriate response under "list_behavior".  It does not look under
"nlist_behavior".

On a DEC-20, the Sieve algorithm as programmed originally generated
only 11 primes (till 31) before running out of space.  The algorithm
when reprogrammed as above generated 103 primes (till 563) before
running out of space, and ran three times as efficiently.

-- Sanjai Narain

------------------------------

Date: Thursday, 2 June 1983, 01:07-EDT
From: Henry Lieberman <Henry%MIT-OZ@MIT-MC>
Subject: Object-Oriented Programming in Prolog, reply to Narain

Sanjai,

Thank you for your informative reply. My intention was just to
distinguish the claim "Prolog is a good language for implementing
object-oriented programming languages", which your arguments support,
from the claim "Prolog IS a good object-oriented programming
language". The first claim I am sympathetic to, because of the
features you cite, but the second I am skeptical about, since all such
attempts I've seen involve adding additional interpretive mechanism
and changing programming style conventions rather drastically.  I
believe Kahn's original intent in Intermission was to support the
first claim rather than the second.

The problem with the "hundred flowers" approach to supporting multiple
programming styles in the same language (efficiency aside) is
confusion for the poor user. In your scheme, a programmer might start
out thinking "I'm not going to use the hairy features of Actors, why
don't I just program directly in Prolog" and writes predicates like
Above(Block-1, Block-2). Then later in the evolution of the system,
the need for default information arises, so all previous code has to
be rewritten to say Sent(Block-1, Above, Block-2, True). This comes up
all the time in Lisp-embedded object-oriented programming [Flavors,
Loops, etc.] The point is if you're convinced enough about the virtues
of object-oriented programming, you might as well make your language
use it for everything.

I like your optimization to Kahn's message reception scheme, it works
out very nicely. Something I think Prolog-based object-oriented
languages might be promising for is in dealing with the so-called
"multiple-inheritance problem" which has been a difficult point in
other languages so far. Perhaps it would be possible in such a scheme
to just add some more clauses involving Ancestor and Parent relations
to resolve inheritance conflicts.

-- Henry

------------------------------

End of PROLOG Digest
********************