Object Oriented Programming

Author(s): Angel Fernandez Pineda.

O'Ciao is a set of libraries which allows object-oriented programming in Ciao Prolog. It extends the Ciao Prolog module system by introducing two new concepts:

  • Inheritance.
  • Instantiation.

Polymorphism is the third fundamental concept provided by object oriented programming. This concept is not mentioned here since traditional PROLOG systems are polymorphic by nature.

Classes are declared in the same way as modules. However, they may be enriched with inheritance declarations and other object-oriented constructs. For an overview of the fundamentals of O'Ciao, see http://www.clip.dia.fi.upm.es/~clip/papers/ociao-tr.ps.gz. However, we will introduce the concepts in a tutorial way via examples.

Early examples

The following one is a very simple example which declares a class -- a simple stack. Note that if you replace class/1 declaration with a module/1 declaration, it will compile correctly, and can be used as a normal Prolog module.

%%----------------------------------------------%%
%% A class for stacks.                          %%
%%----------------------------------------------%%

%% Class declaration: the current source defines a class.
:- class(stack,[],[]).

% State declaration: storage/1 is an attribute.
:- dynamic storage/1.

% Interface declaration: the following predicates will
% be available at run-time.
:- export(push/1).
:- export(pop/1).
:- export(top/1).
:- export(is_empty/0).

% Methods

push(Item) :-
	nonvar(Item), 
	asserta_fact(storage(Item)).

pop(Item) :-
	var(Item),
	retract_fact(storage(Item)).

top(Top) :-
	storage(Top), !.

is_empty :-
	storage(_), !, fail.
is_empty.

If we load this code at the Ciao toplevel shell:

        ?- use_package(objects).

        yes
        ?- use_class(library('class/examples/stack')).

        yes
        ?-

we can create two stack instances :

        ?- St1 new stack,St2 new stack.

        St1 = stack('9254074093385163'),
        St2 = stack('9254074091') ? ,

and then, we can operate on them separately:

        1 ?- St1:push(8),St2:push(9).

        St1 = stack('9254074093385163'),
        St2 = stack('9254074091') ? 

        yes
        1 ?- St1:top(I),St2:top(K).

        I = 8,
        K = 9,
        St1 = stack('9254074093385163'),
        St2 = stack('9254074091') ? 

        yes
        1 ?-

The interesting point is that there are two stacks. If the previous example had been a normal module, we would have a stack , but only one stack.

The next example introduces the concepts of inheritable predicate, constructor, destructor and virtual method. Refer to the following sections for further explanation.

%%----------------------------------------------%%
%% A generic class for item storage.            %%
%%----------------------------------------------%%
:- class(generic).

% Public interface declaration:
:- export([set/1,get/1,callme/0]).

% An attribute
:- data datum/1.

% Inheritance declaration: datum/1 will be available to 
% descendant classes (if any).
:- inheritable(datum/1).

% Attribute initialization: attributes are easily initialized
% by writing clauses for them.
datum(none).

% Methods

set(X) :-
        type_check(X),
        set_fact(datum(X)).

get(X) :-
        datum(X).

callme :-
	a_virtual(IMPL),
	display(IMPL),
	display(' implementation of a_virtual/0 '),
	nl.

% Constructor: in this case, every time an instance
% of this class is created, it will display a message.
generic :-
	display(' generic class constructor '),
	nl.

% Destructor: analogous to the previous constructor,
% it will display a message every time an instance
% of this class is eliminated.
destructor :-
	display(' generic class destructor '),
	nl.

% Predicates:
% cannot be called as messages (X:method)

% Virtual declaration: tells the system to use the most
% descendant implementation of a_virtual/1 when calling
% it from inside this code (see callme/0).
% If there is no descendant implementation for it, 
% the one defined bellow will be used.
:- virtual a_virtual/1.

a_virtual(generic).

:- virtual type_check/1.

type_check(X) :-
	nonvar(X).

And the following example, is an extension of previous class. This is performed by establishing an inheritance relationship:

%%----------------------------------------------%%
%% This class provides additional functionality %%
%% to the "generic" class.                      %%
%%----------------------------------------------%%
:- class(specific).

% Establish an inheritance relationship with class "generic".
:- inherit_class(library('class/examples/generic')).

% Override inherited datum/1.
% datum/1 is said to be overriden because there are both an
% inherited definition (from class "generic") and a local one,
% which overrides the one inherited.
:- data datum/1. 
:- inheritable datum/1.

% Extend the public interface inherited from "generic".
% note that set/1 and a_virtual/0 are also overriden. 
% undo/0 is a new functionality added.
:- export([set/1,undo/0]).

% Methods

set(Value) :-
	inherited datum(OldValue),
	!,
	inherited set(Value),
	asserta_fact(datum(OldValue)).
set(Value) :-
	inherited set(Value).

undo :-
        retract_fact(datum(Last)), !,
        asserta_fact(inherited(datum(Last))).
undo :-
	retractall_fact(inherited(datum(_))).

% Constructor
specific :-
	generic,
	retractall_fact(inherited(datum(_))),
	display(' specific class constructor '),
	nl.

% Destructor
destructor :-
	display(' specific class destructor '),
	nl.

% Predicates

% New implementation of a_virtual/1. 
% Since this predicate was declared virtual, the
% implementation below will be called from the inherited 
% method callme/0 instead of the version defined at "generic".
a_virtual(specific).

Additional examples may be found on the library/class/examples directory relative to your Ciao Prolog instalation.

Recommendations on when to use objects

We would like to give some advice in the use of object oriented programming, in conjunction with the declarative paradigm.

You should reconsider using O'Ciao in the following cases:

  • The pretended "objects" have no state,i.e., no data or dynamic predicates. In this case, a normal module will suffice.

  • There is state, but there will be only one instance of a pretended class. Again, a module suffices.

  • The "objects" are data structures (list,trees,etc) already supported by Prolog. However, it does make sense to model, using objects, data structures whose change implies a side-effect such as drawing a particular window on the screen.

We recommend the usage of O'Ciao in the following cases:

  • You feel you will need to have several copies of a "module".
  • Local copies of a module are needed instead of a global module beeing modified by several ones.
  • The "classes" are a representation of external entities to Prolog. For example: the X-Window system.
  • There is state or code outside the Prolog system which needs to be manipulated. For example: interfaces to Java or Tcl/Tk code.
  • You are not familiar with Prolog, but you know about object oriented programming. O'Ciao may be used as a learning tool to introduce yourself on the declarative programming paradigm.

Limitations on object usage

O'Ciao run-time speed is limited by the usage of meta-programming structures, for instance: X = (Object:mymethod(25)), call(X). O'Ciao will optimize static manipulation of objects (those that can be determined at compile time).