Pound Example

In this example, we demonstrate object-oriented programming. Object-oriented programming has to be distinguished from object-oriented modelling. In object-oriented programming the classes of objects are directly represented as program text. We will make use of the ISO module standard and represent classes as Prolog module texts.

The anatomy of an instance object will be such that it will be simply a Prolog term. The functor of the Prolog term will indicate the class of the instance object and the arguments will be the actual parameters of the instance object. The methods of the class will be stored in a Prolog module text with the same name as the class of the object:

To invoke methods on instance objects Jekejeke Prolog then provides the operator (::)/2. This operator is bootstrapped from ordinary module operator (:)/2. It will first determine the class of the instance object, then add the instance object as a new first argument to the message and finally invoke the message in the determined class.

We now consider a dog pound. We assume that there is a class dog that determines the behaviour of dogs through its methods. Further, we assume that a dog instance has a name directly found in the first parameter. The method bark/1 is a dog command. The method barking/2 is used to retrieve the barking sound of a dog.

:- module(dog, [bark/1, barking/2]).

bark(Self) :-
arg(1, Self, Name),
write(Name), write(' says '),
write(Barking), write('.'), nl.

barking(_, ruff).

Both methods are implemented so that they adhere to the convention of the Jekejeke Prolog operator (::)/2. Namely they have a first argument for the instance object itself. This convention was adopted from Python, which features the same manner of defining methods. We can now send the bark command to different dogs:

?- dog(susi)::bark.
susi says ruff.

?- dog(strolch)::bark.
strolch says ruff.

Before consulting the above Prolog text, make sure to have the path set via sys_add_path/1. Further place a package directive use_package/1 into the Prolog text and before top-level queries, so that short class names can be used. We now consider programming of sub classes. The ISO module standard reexport/1 directive serves as an inheritance indicator:

The Jekejeke Prolog (::)/2 is already polymorphic. The reexport/1 directive will make all the methods of the parent class of a subclass visible in the subclass itself. This allows for the additional benefit of code sharing in a parent class of a subclass if the code is common for multiple subclasses. We will implement a class basset with a different barking:

:- module(basset, [barking/2]).
:- reexport(dog).

:- override barking/2.
barking(_, woof).

In the above Prolog text, the directive override/1 is an extension by Jekejeke Prolog and is necessary to suppress the warning that a re-exported predicate is overridden. However, this exactly what we want to do in the present example, implement a different barking. We can send the bark command to a basset dog.

?- basset(lafayette)::bark.
lafayette says woof.

Classes defined as above retain all the advantages of the Jekejeke Prolog module system. They can be automatically reloaded by the make/0 command. Further, it is possible to debug classes as if they were ordinary Prolog texts. Since methods are Pythonesk predicates, it carries over to set spy points and break points without restrictions.