Go to the first, previous, next, last section, table of contents.


Declaring classes and interfaces

Author(s): Angel Fernandez Pineda.

Version: 1.11#222 (2004/5/24, 13:8:7 CEST)

Version of last change: 1.7#162 (2001/12/4, 16:2:58 CET)

O'Ciao classes are declared in the same way as traditional prolog modules. The general mechanism of source expansion will translate object-oriented declarations to normal prolog code. This is done transparently to the user.

Abstract interfaces are restricted classes which declare exported predicates with no implementation. The implementation itselt will be provided by some class using an implements/1 declaration. Only export/1 and data/1 declarations are allowed when declaring an interface. Normal classes may treated as interfaces just ignoring all exported predicate implementations.

Usage and interface (class)

Documentation on new declarations (class)

DECLARATION: export/1:

Declares a method or attribute to be part of the public interface.

The public interface is the set of predicates wich will be accesible from any code establishing an usage relationship with this class (see use_class/1 for further information).

Publishing an attribute or method is very similar to exporting a predicate in a Prolog module.

Whether an inherited and exported predicate is overriden, it must be explicitly exported again.

An inherited (but not exported) predicate may become exported, without overriding it by the usage of this declaration.

Usage: :- export(Spec).

DECLARATION: public/1:

Just an alias for export/1.

Usage: :- public(Spec).

DECLARATION: inheritable/1:

Declares a method or attribute to be inherited by descendant classes. Notice that all public predicates are inheritable by default. There is no need to mark them as inheritable.

Traditionaly, object oriented languages makes use of the protected concept. Inheritable/1 may be used as the same concept.

The set of inheritable predicates is called the inheritable interface.

Usage: :- inheritable(MethodSpec).

DECLARATION: data/1:

Declares an attribute at current class. Attributes are used to build the internal state of instances. So, each instance will own a particular copy of those attribute definitions. In this way, one instance may have different state from another.

O'Ciao attributes are restricted to hold simple facts. It is not possible to hold a Head :- Body clause at an instance attribute.

Notice that attributes are multi-evaluated by nature, and may be manipulated by the habitual assert/retract family of predicates.

Attributes may also be initialized. In order to do so, simply put some clauses after the attribute definition. Each time an instance is created, its initial state will be built from those initialization clauses.

Note: whether a data/1 declaration appears inside an interface, it will be automatically exported.

Usage: :- data Spec.

DECLARATION: dynamic/1:

Just an alias for data/1.

Usage: :- dynamic Spec.

DECLARATION: concurrent/1:

Declares a concurrent attribute at current class. Concurrent attributes are just the same as normal attributes, those declared using data/1, except for they may freeze the calling thread instead of failing when no more choice points are remaining on the concurrent attribute.

In order to get more information about concurrent behavior take a look to the concurrent/1 built-in declaration on Ciao Prolog module system.

Usage: :- concurrent Spec.

DECLARATION: inherit_class/1:

Makes any public and/or inheritable predicate at inherited class to become accesible by any instance derived from current class.

Inherited class is also called the super class.

Only one inherit_class/1 declaration is allowed to be present at current source.

Notice that inheritance is public by default. Any public and/or inheritable declaration will remain the same to descendant classes. However, any inherited predicate may be overriden (redefined).

A predicate is said to be overriden when it has been inherited from super class, but there are clauses (or a data/1 declaration) present at current class for such a predicate.

Whether a public predicate is overriden, the local definition must also be exported, otherwise an error is reported.

Whether an inheritable predicate (not public) is overriden, the local definition must also be marked as inheritable or exported, otherwise an error is also reported.

Note: whether inherit_class/1 appears inside an interface, it will be used as an implements/1 declaration.

Usage: :- inherit_class(Source).

DECLARATION: implements/1:

Forces current source to provide an implementation for the given interface file. Such interface file may declare another class or a specific interface.

Every public predicate present at given interface file will be automatically declared as public at current source, so you must provide an implementation for such predicates.

The effect of this declaration is called interface inheritance,and there is no restriction on the number of implements/1 declarations present at current code.

Usage: :- implements(Interface).

DECLARATION: virtual/1:

This declaration may be used whenever descendant classes are to implement different versions of a given predicate.

virtual predicates give a chance to handle, in an uniform way, different implementations of the same functionality.

Whether a virtual predicate is declared as a method, there must be at least one clause of it present at current source. Whenever no special implementation is needed at current class, a never-fail/allways-fail clause may be defined (depending on your needs). For example:

   :- virtual([ test1/1 , test2/2 ]).
   test1(_).
   test2(_,_) :- fail.

This kind of virtual methods are also known as abstract methods, since implementation is fully delegated to descendant classes.

An attribute may be also declared as a virtual one, but there is no need to write clauses for it.

Usage: :- virtual(VirtualMethodSpec).

Documentation on exports (class)

PREDICATE: inherited/1:

This predicate qualificator may be used whenever you need to reference an attribute or method on the super class.

Since methods and attributes may be redefined, this qualificator is need to distinguish between a locally declared predicate and the inherited one, which has the same name.

There is no need to use inherited/1 if a particular inherited predicate has not been redefined at current class.

Usage: inherited(Goal)

PREDICATE: self/1:

Determines which instance is currently executing self/1 goal.

Predicate will fail if argument is not a free variable. Otherwise, it will allways succeed, retrieving the instance identifier which is executing current code.

This functionality is very usefull since an object must have knowledge of other object's identifier in order to send messages to it.For example:

:- concurrent ack/0.

send_data_to_object(Data,Obj) :- self(X), Obj:take_this(Data,X), current_fact(ack).

acknowledge :- asserta_fact(ack).

take_this(Data,Sender) :- validate_data(Data), Sender:acknowledge.

Usage: self(Variable)

PREDICATE: constructor/0:

A constructor is a special case of method which complains the following conditions:

This is a simple example of constructor declaration for the foo class:

           foo :- 
               display('an instance was born').

Constructor declaration is not mandatory, and there may be more than one constructor declarations (with different arity) at the source code.

This functionality is usefull when some computation is needed at instance creation. For example: opening a socket, clearing the screen, etc.

Whenever an inheritance relationship is established, and there is any constructor defined at the super class, you must call manually an inherited constructor. Here is an example:

           :- class(foo).
           :- inherit_class(myclass).

           foo :-
               myclass(0),
               display('an instance was born').

           foo(N) :- myclass(N).

Consequences may be unpredictable, if you forget to call an inherited constructor. You should also take care not to call an inherited constructor twice.

All defined constructors are inheritable by default. A constructor may also be declared as public (by the user), but it is not mandatory.

Usage:

PREDICATE: destructor/0:

A destructor is a special case of method which will be automatically called when instance destruction takes place.

A destructor will never be wanted to be part of the public interface, and there is no need to mark them as inheritable, since all inherited destructors are called by O'Ciao just before yours.

This is a simple example of destructor declaration:

           destructor :- 
               display('goodbye, cruel world!!!').

Destructor declaration is not mandatory. Failure or sucess of destructors will be ignored by O'Ciao, and they will be called only once.

This functionality is useful when some computation is need at instance destruction. For example: closing an open file.

Usage:

Other information (class)

This describes the errors reported when declaring a class or an interface. The first section will explain compile-time errors, this is, any semantic error which may be determined at compile time. The second section will explain run-time errors, this is, any exception that may be raisen by the incorrect usage of O'Ciao. Some of those errors may be not reported at compile time, due to the use of meta-programational structures. For example:

functor(X,my_method,0),call(X).

O'Ciao is not able to check whether my_method/0 is a valid method or not. So, this kind of checking is left to run time.

Class and Interface error reporting at compile time

Class and Interface error reporting at run time

Normal Prolog module system interaction

O'Ciao works in conjunction with the Ciao Prolog module system, which also reports its own error messages. This will cause Ciao to report a little criptic error messages due to the general mechanism of source-to-source expansion. Those are some tips you must consider when compiling a class:

Known bugs and planned improvements (class)


Go to the first, previous, next, last section, table of contents.