A class implementation is used to provide the definitions of the predicates and constructors declared in the class declaration, as well as the definitions of any predicates supported by its constructed objects.
A class can privately (i.e. inside the implementation) declare and define more entities than those mentioned in the declaration. Especially, an implementation can declare fact databases that can be used to carry class and object state.
An implementation is a mixed scope, in the sense that it both contains the implementation of the class and of the objects produced by the class. The class part of a class is shared among all objects of the class, as opposed to the object part, which is individual for each object. Both the class part and the object part can contain facts and predicates, whereas domains, functors and constants always belong to the class part, i.e. they do not belong to individual objects.
By default all predicate and fact members declared in the implementation of a class are object members. To declare class members the section keyword (i.e. predicates and facts) must be prefixed with the keyword class. All members declared in such sections are class members.
Class members can reference the class part of a class, but not the object part.
Object members, on the other hand, can access both the class part and the object part of the class.
In the code in the implementation, the owner object is subsumed by all object predicates. The subsumed owner object can also be accessed directly through the special variable "This".
ClassImplementation : implement ClassName ScopeQualifications Sections end implement ClassName-opt
See also Generic Interfaces and Classes.
The ClassName in the end of the class implementation must (if present) be identical to the one in the beginning of the class implementation.
The ScopeQualifications must be of the kinds:
A Supports qualification states the list of interfaces, which are supported privately by the class implementation.
A Delegate qualification delegates functionality of (object) predicates from interfaces to predicates from objects, which can be stored as fact variables.
The Sections must be of the kinds:
constructorsSections are only legal if the class ClassName states a ConstructionType. Classes that states a ConstructionType are also object constructors, which construct objects of the stated construction type.
This example illustrates how class facts are shared among the objects of the class and how object facts are not shared.
Consider the interface aa and class aa_class:
interface aa predicates setClassFact : (integer Value). getClassFact : () -> integer. setObjectFact : (integer Value). getObjectFact : () -> integer. end interface class aa_class : aa end class
The point of the predicates is that they store and fetches values from respectively class and object facts:
implement aa_class class facts classFact : integer := 0. facts objectFact : integer := 0. clauses setClassFact(Value) :- classFact := Value. getClassFact() = classFact. clauses setObjectFact(Value) :- objectFact := Value. getObjectFact() = objectFact. end implement aa_class
Given this class consider the goal:
goal A1 = aa_class::new(), A2 = aa_class::new(), A1:setClassFact(1), A1:setObjectFact(2), ClassFact = A2:getClassFact(), ObjectFact = A2:getObjectFact().
The class fact is shared among all objects, so setting the class fact via A1 also affects the value obtained via A2. Hence, the value of ClassFact will be one, the value set via A1.
On the other hand, object facts belong to each object. Therefore setting the object fact in A1 will not affect the value stored in A2. Hence value of ObjectFact is zero, the value that the fact was initialized to in A2.
This section describes object construction and, as such, it only deals with classes that produce objects.
Objects are constructed by calling a constructor.
Constructors are explicitly declared in constructors sections in class declarations and implementations (see also Default Constructor).
A constructor actually has two associated predicates:
- A class function, which returns a new constructed object.
- An object predicate, which is used when initializing inherited objects.
The associated object predicate is used to perform initialization the object. This predicate can only be called from a constructor in the class itself and from a constructor in a class, which inherits from the class (i.e. base class initialization).
The associated class function is defined implicitly, i.e. there are no clauses for it anywhere.
The class function allocates memory to hold the object, perform internal initialization of the object and then invokes the object constructor on the created object. Finally, the constructed object is returned as the result of the constructor execution.
So before the clauses of the constructor are invoked:
- All object facts variables that have an initialization expression are initialized.
- All object facts that have clauses are initialized from these clauses.
This initialization is also performed on all (transitively) inherited sub-objects, before the clauses of the constructor are invoked.
The constructor clauses must:
- Initialize all those single object facts and object fact variables that are not initialized before entrance.
- Initialize all sub-objects.
The constructor clauses can do other things as well, but it must perform the initialization mentioned here to ensure that the object is valid after construction.
Note. During construction objects might not be valid and care must be taken not to access un-initialized parts of the object (see Rules for Constructing Objects).
A default constructor is a null-ary constructor with the name new/0. If a class that constructs objects does not declare any constructors in the class declaration, then the default constructor (i.e. new/0) is implicitly declared (in the class declaration). This means that each class has at least one constructor. So writing:
class aaa end class aaa
is exactly the same as writing
class aaa constructors new : (). end class aaa
It is legal to re-declare the default constructor explicitly.
It is not necessary to define (i.e. implement) the default constructor; if it is not defined then an effect-less definition is implicitly assumed. So writing
implement aaa end implement aaa
is exactly the same as writing:
implement aaa clauses new(). end implement aaa
(Given that aaa has a default constructor).
Notice that a class has a default constructor if and only if:
- it does not (publicly) declare any constructors at all;
- or it declares new/0 as a constructor.
Which is the same (negated) as: A class does not have a default constructor if:
- It publicly declares constructors;
- and it does not publicly declare new/0 as a constructor.
Given an interface aa, consider the following code:
class aa_class : aa end class
The class aa_class declares no constructors; therefore, it implicitly declares the default constructor. Thus you can create an aa_class object like:
goal _A = aa_class::new(). % implicitly declared default constructor
It is legal to implement the implicitly declared default constructor of aa_class:
implement aa_class clauses new() :- ... end implement
The bb_class class explicitly declares a constructor, which is not the default constructor; subsequently the class does not have the default constructor
class bb_class : aa constructors newFromFile : (file File). end class
The cc_class class declares the newFromFile/1 constructor, but it also declares the default new/0 constructor; so obviously, it has the default new/0 constructor
class cc_class : aa constructors new : (). % default constructor newFromFile : (file File). end class
You also can declare "private" constructors in class implementations. This can be reasonable, for example, in the following situations:
- When some predicate returns an object of construction type, then in a class implementation can be declared, implemented and called a "private" constructor to create such objects.
- When some class declares several "public" constructors having a "same big part", then it can be reasonable to define in the class implementation a "private" constructor, which implements this "same big part". Then clauses of all these "public" constructors can simply call this "private" constructor to implement this "same big part".
Notice that if a class, which can construct objects, does not declare any constructors in the class declaration, then the default constructor (i.e. new/0) will be declared implicitly independently of whether the class implementation declares or not "private" constructors. That is, it is possible to write:
interface aa end interface class aa : aa end class implement aa constructors myCreate : (). clauses myCreate() :- ... end implement % program code ... Obj = aa::new(), % This is the declared IMPLICIT default class constructor ...
All constructors are responsible for initializing constructed objects to valid states. In order to obtain such a valid state all sub-objects (i.e. inherited classes) must be initialized as well.
The sub-objects can be initialized in one of two ways, either the programmer calls a constructor of the inherited class or the default constructor is automatically invoked. The latter requires that the inherited class actually has the default constructor, but this is no difference whether this default constructor is declared explicitly or implicitly - it will be called in both cases!
If the inherited class does not have a default constructor, then another constructor must be invoked explicitly. The default invocation of constructors of inherited classes takes place immediately after initialization of fact variables and facts with clauses and before entrance to the clauses of the constructor.
Constructors of inherited classes are invoked by the versions that does not return a value. If you call the version that returns a value then you are actually creating a new object, rather than invoking the constructor on "This" (see the example below).
This implementation of the class bb_class inherits from the class aa_class and the default constructor of bb_class call a constructor of aa_class with a newly created cc_class object
implement bb_class inherits aa_class clauses new() :- C = cc_class::new(), % create a cc_class object aa_class::newC(C). % invoke constructor on inherited sub-object ... end implement
If a base class is not explicitly constructed, then it is implicitly constructed using the default constructor. So writing:
implement bbb inherits aaa clauses myNew() :- doSomething(). end implement bbb
is exactly the same as writing:
implement bbb inherits aaa clauses myNew() :- aaa::new(), doSomething(). end implement bbb
If aaa does not have a default constructor then this will, of course, give an error.
Notice that this rule (of course) can be combined with the rule discussed in the first paragraph of Default Constructor. So writing:
implement bbb inherits aaa end implement bbb
Is exactly the same as writing (according to the rule discussed in the first paragraph of Default Constructor):
implement bbb inherits aaa clauses new(). end implement bbb
Which is exactly the same as writing (the rule discussed above):
implement bbb inherits aaa clauses new() :- aaa::new(). end implement bbb
Single (Object) Fact Initialization
Just like all constructors have to initialize/construct sub-objects, they also have to initialize all single facts of an object, before they are referenced the first time.
Notice that single class facts can only be initialized with clauses, since they are not related to an object. A class fact can be accessed before the first object is created.
This example shows (1) how to initialize a fact variable by means of an expression; (2) how to initialize a single fact (point) by means of a clause; (3) how to initialize a single fact in the constructor and (4) where the default constructor of an inherited class is invoked:
implement bb_class inherits aa_class facts counter : integer := 0. point : (integer X, integer Y) single. c : (cc C) single. clauses point(0, 1). % The object is created and counter and point are initialized before entrance, % the default constructor aa_class::new/0 is also invoked before entrance new() :- C = cc_class::new(), assert(c(C)). ... end implement
Construction by Delegation
As an alternative to construct the object directly in a constructor the job can be delegated to another constructor of the same class. This is done simply by invoking the other constructor (i.e. the version that does not return a value). When delegating construction we have to be sure that the object is actually constructed and that it is not "over-constructed". Single facts can be assigned a value as many times as one likes so they cannot be "over-constructed". Inherited classes, on the other hand, may only be initialized once during object construction.
This example shows a typical use of construction by means of delegation. A constructor (new/0) invokes another constructor (newFromC/1) with a default value.
implement aa_class facts c : (cc C) single. clauses new() :- C = cc_class::new(), newFromC(C). newFromC(C) :- assert(c(C)). ... end implement
Rules for Constructing Objects
The programmer must ensure that:
- All sub-objects are initialized/constructed exactly once each.
- All single facts are initialized (at least once).
- That no sub-object is referenced before it is initialized/constructed.
- That no single fact is used before it is initialized.
There is no guarantee about how clever the compiler is in detecting such problems at compile time.
The compiler may offer to generate runtime validation, it may also offer to non-safely omit such runtime validations.
An object predicate is always invoked on an object. This object carries the object facts and is subsumed by the implementation of object predicates. The object predicate has access to this implicit object. We shall call the object "This". There are two kinds of access to "This" implicit and explicit.
In every clause of every object predicate the variable This is implicitly defined and bound to "This", i.e. the object whose member predicate is executing.
In the clause of an object member predicate other object member predicates can be called directly, because "This" is implicitly assumed for the operation. Members of super-classes can also be invoked directly, as long as it is unambiguous which method is called (see Scoping & Visibility). Likewise the object facts (which are stored in "This") can be accessed.
"This" and Inheritance
[Notice this particular section is subject to change, as it is decided to change the language semantics in this matter.]
"This" always refer to an object belonging to the class in which "This" is used, also if that class is inherited by another class.
Assume the interface aa declared as follows:
interface aa predicates action : (). doAction : (). end interface
also assume the class aa_class declared as follows:
class aa_class : aa end class
And implemented as follows:
implement aa_class clauses action() :- doAction(), This:doAction(). doAction() :- write("aa_class::doAction"), nl. end implement
The following goal:
goal A = aa_class::new(), A:action().
Now also consider class bb_class declared as follows:
class bb_class : aa end class
And implemented as follows
implement bb_class inherits aa_class clauses doAction() :- write("bb_class::doAction"), nl. end implement
The following goal:
goal B = bb_class::new(), B:action().
Will also write:
because (both implicit and explicit) "This" in aa_class refers to the object of class aa_class.
Now consider else one example:
interface iName predicates className : () -> string Class. name: () -> string Class. nameThis: () -> string Class. end interface iName class aaa : iName end class aaa implement aaa clauses className() = "aaa". clauses name() = className(). clauses nameThis() = This:className(). end implement aaa class bbb : iName end class bbb implement bbb inherits aaa clauses className() = "bbb". end implement bbb goal OOO = bbb::new(), Class1 = OOO:name(), Class2 = OOO:nameThis().
Class bbb inherits the definition of name and nameThis from aaa, but it re-implements className. In the goal we create a bbb object and on this we call name and nameThis.
The name predicate calls className directly, while nameThis calls This:className. The effects are different. The name predicate calls className, which is defined in aaa. But nameThis call the className that is defined on This and that is actually bbb::className, since This is a bbb object.
All in all Class1 is bound to the value "aaa", whereas Class2 is bound to "bbb".
Calling from a parent class to a child class like in nameThis is very often used to implement general functionality in a shared parent class. The general functionality relies on the refinements of the child classes for the non-generalized functionality.
Also consider this class:
interface iTest predicates test : () -> string Name. end interface iTest class ccc : iTest end class ccc implement ccc inherits aaa clauses test() = aaa::nameThis(). end implement ccc
ccc also inherits from aaa. Since ccc inherits from aaa it also implicitly supports the iName interface (i.e. privately). So when aaa calls This:className it call the one that ccc provides, which happens to be the one inherited from aaa.
Also consider this example:
class ddd : name end class ddd implement ddd inherits bbb clauses className() = "ddd". end implement ddd goal OOO = ddd::new(), Class1 = OOO:name(), Class2 = OOO:nameThis().
ddd inherits from bbb which we know inherits from aaa. When calling nameThis we actually call aaa::nameThis, which calls This:className. In this case This is the ddd object and therefore the effective call will go to ddd::className.
All in all a call to This:className goes to last child that supports an interface containing className.
Inherits qualifications are used to state that an implementation inherits from one or more classes. Inheritance has only influence on the object part of a class.
The purpose of inheriting from other classes is to inherit behavior from these classes.
When a class cc inherits from a class aa, this means that the implementation of the cc class automatically implicitly (privately) supports the construction type (interface) of the aa class. (If the class aa implementation already support the cc construction type explicitly then there is of course no difference.)
Therefore, notice that the same predicate, for example p, cannot be declared in the construction type interface of a cc class and in the construction type interface of an inherited aa class. (The compiler will detect this and generates an error that a predicate is declared in 2 places.) Let us discuss this in some details. Let us suppose that a cc class has a construction type interface cci and some other aa class has a construction type interface aai. Let both aai and cci interfaces declare the same predicate p. Till the aa and cc classes are independent, the compiler does not detect any problems. But as soon as we declare that the cc class inherits from the aa class, then cc class starts to support also the aai interfaces. Therefore, the cc class starts to see both declarations of the predicate p, which will be reported as a compiling time error. The only possibility to avoid such ambiguity in the predicate p declaration is using of the Predicates from Interface section in the cci interface declaration. For example like:
interface cci predicates from aai p(), ... end interface cci
Object predicates can be inherited: If the class does not implement a certain of its object predicates, but one of the classes it inherits from does implement this predicate, then that predicate will be used for the current class.
The class that inherits from another class does not have any special privileges towards the inherited class: It can only access the embedded object through its construction type interface.
Inheritance must be unambiguous. If the class defines the predicate itself then there is no ambiguity, because then it is this predicate definition that is exposed. If only one inherited class supports the predicate then it is also unambiguous. But if two or more classes supports the predicate then it is ambiguous which class provides the definition. In that case the ambiguity must be resolved by means of a resolve qualification (see Resolve Qualification).
Object predicates from inherited classes can be called directly from object predicates in the current class, since the embedded sub-object is implicitly used as predicate owner. Class qualification can be used to resolve calling ambiguities for object predicates from inherited classes.
InheritsQualification : inherits ClassName-comma-sep-list
As mentioned elsewhere all ambiguities related to calling predicates can be avoided by using qualified names.
But when it comes to inheritance this is not the case. Consider the following example:
interface aa predicates p : () procedure (). ... end interface class bb_class : aa end class class cc_class : aa end class class dd_class : aa end class implement dd_class inherits bb_class, cc_class end implement
In this case it is ambiguous which of the classes bb_class and cc_class that would provide the implementation of aa for dd_class. (Notice that when we say that a class implements an interface, it means that it provide definitions for the predicates declared in the interface.)
It would of course be possible to add clauses to the implementation of dd_class, which would effectively solve the job. Consider, for example, the following clause, which would "export" the predicate p from bb_class:
clauses p() :- bb_class::p().
But, with this code we have not really inherited the behavior from bb, we have actually delegated the job to the bb_class part of our class.
So to resolve this kind of ambiguities (and use real inheritance rather than delegation) we use a resolve section. A resolve section contains a number of resolutions:
ResolveQualification : resolve Resolution-comma-sep-list
Resolution : InterfaceResolution PredicateFromClassResolution PredicateRenameResolution PredicateExternallyResolution
A resolve qualification is used to resolve an implementation from the specified source.
PredicateFromClassResolution : PredicateNameWithArity from ClassName
A predicate from class resolution states that the predicate is implemented by the specified class.
To resolve a predicate to a class:
- the class must implement the predicate to be resolved, this implies that the predicate must origin in the same interface as the one that should be inherited
- the class must be mentioned in the inherits section
Predicate Rename Resolution
A predicate rename resolution states that the predicate is implemented as predicate with another name. The predicate must come from an inherited class and its type, mode and flow must match exactly.
PredicateRenameResolution : PredicateNameWithArity from ClassName :: PredicateName
An interface resolution is used to resolve a complete interface from one of the inherited classes. Thus an interface resolution is a short way of stating that all the predicates in the interface should be resolved from the same class.
InterfaceResolution : interface InterfaceName from ClassName
The class must publicly support the resolved interface.
If both a predicate resolution and an interface resolution cover some predicate name, then the predicate resolution is used. I.e. the specific resolution overrides the less specific ones.
It is valid for a predicate to be covered by several interface resolutions, as long as these all resolve the predicate to the same class. If on the other hand a predicate is resolved to different classes by interface resolutions, then the resulting ambiguity must be resolved by a predicate resolution.
Note: The syntax of resolutions is not capable of resolving different overloading of a predicate to different classes.
We can solve the ambiguity from the example above by providing an interface resolution. In this case we have chosen to inherit the implementation of aa_class from cc_class, except that we will inherit p from bb_class
implement dd_class inherits bb_class, cc_class resolve interface aa from cc_class p from bb_class end implement
A predicate externally resolution states that the predicate is not at all implemented in the class itself, but in an external library. External resolutions can only be used for class predicates. I.e. object predicates cannot be resolved externally.
It is important that the calling convention, link name and argument types correspond to the implementation in the library.
Both private and public predicates can be resolved externally.
PredicateExternallyResolution : PredicateNameWithArity externally
Dynamic External Resolution
A predicate externally resolution also provide syntax for dynamic loading of private and public class predicates from DLLs.
The syntax is:
PredicateExternallyResolutionFromDLL : PredicateNameWithArity externally from DllNameWithPath
DllNameWithPath : StringLiteral
If the predicate predicateNameWithArity is missed in the DLL DllNameWithPath, then the dynamic loading provides the possibility to run a program until it actually invokes the missed predicate. The runtime error will occur on such invocation. The DllNameWithPath is the path to the DLL on the machine where the program should run. One can absolute or relative. For example if the required dll is situated in the one level up from directory which the application loaded, then the DllNameWithPath should be like "../DllName". See also Dynamic Library Search Order in MSDN.
Once an object cannot be reached by the program it can be finalized, the semantics of the language does not say exactly when the object will be finalized. The only thing that is guaranteed is that it is not finalized as long as it can be reached from the program. In practice the object is finalized, when it is wasted by the garbage collector. Finalization is the opposite of construction and will remove the object from memory.
Classes can also implement a finalizer, which is a predicate that is invoked when the object is finalized (before it is removed from memory).
A finalizer is a procedure with no arguments and no return value, which has the name finalize. The predicate is implicitly declared and cannot be invoked directly from the program.
The main purpose of finalizers is to be able to release external resources, but there are no restrictions on what it can do. Finalizers should however be used with caution, recall that the time of their invocation is not completely known and, therefore, it might also be difficult to predict the overall program state at the time where they are invoked.
Notice that there is no reason to retract object facts from an object in the finalizer, because this is automatically done in the finalization process.
All objects are finalized before the program can terminate (unless an abnormal situation like power failure prevents this).
This example uses a finalizer to ensure that a database connection is closed properly.
implement aa_class facts connection : databaseConnection. clauses finalize() :- connection:close(). ... end implement aa_class
A delegate section contains a number of delegations:
DelegateQualification : delegate Delegation-comma-sep-list
Delegation : PredicateDelegation InterfaceDelegation
The delegate qualifications are used to delegate implementations of object predicates to the specified source.
There are two kinds of the delegate qualifications. The Predicate Delegation and the Interface Delegation. The Interface Delegation is used to delegate implementations of a complete set of object predicates declared in an interface to an implementation of another object, stored as fact variable. Thus an interface delegation is a short way of stating that implementations of all predicates in the interface should be delegated to an implementation of the object, stored in fact variable.
The delegate sections look like a correspondent (predicate/interface) resolve sections, except that you delegate to fact variables keeping constructed objects of classes, rather than to inherited classes.
A object predicate delegation states that the predicate functionality is delegated to the predicate in the object specified with the fact variable FactVariable_of_InterfaceType.
PredicateDelegation : PredicateNameWithArity to FactVariable_of_InterfaceType
To delegate a predicate to an object passed with the fact variable:
- The fact variable FactVariable_of_InterfaceType must have a type of an interface (or of its sub-type), which declares the predicate predicateNameWithArity.
- The object supporting the interface must be constructed and be assigned to the fact variable FactVariable_of_InterfaceType.
Consider the following example:
interface a predicates p1 : (). p2 : (). end interface interface aa supports a end interface class bb_class : a end class class cc_class : a end class class dd_class : aa constructors new : (a First, a Second). end class implement dd_class delegate p1/0 to fv1, p2/0 to fv2 facts fv1 : a. fv2 : a. clauses new(I,J):- fv1 := I, fv2 := J. end implement
Later it will be possible to construct objects of the type a and assign them to fact variables fv1 and fv2 to define to objects of which class we really delegate definitions of p1 and p2 functionality. Consider, for example,
goal O_bb = bb_class::new(), O_cc = cc_class::new(), O_dd = dd_class::new(O_bb, O_cc), O_dd : p1(), % This p1 from O_bb object O_dd : p2(). % This p2 from O_cc object
Actually in Visual Prolog delegation has the same effect as if you add clauses to the implementation of dd_class that explicitly specify, from object of which class the predicate functionality is "exported". That is for example, as if the following clause is determined in the implementation of dd_class:
clauses p1() :- fv1:p1().
When you need to specify that functionality of all predicates declared in an interface InterfaceName are delegated to predicates from objects of the same inherited class, you can use the Interface Delegation specification:
InterfaceDelegation : interface InterfaceName to FactVariable_of_InterfaceType
Thus an interface delegation is a short way of stating that functionality of all predicates declared in the interface InterfaceName should be delegated to objects stored as the fact variable FactVariable_of_InterfaceType. Objects should be assigned to the fact variable FactVariable_of_InterfaceType, which should be of the InterfaceName type (or its sub-type).
To delegate an interface to an object passed with the fact variable:
- The fact variable FactVariable_of_InterfaceType must have a type of an interface InterfaceName or of its sub-type.
- The object supporting the interface must be constructed and be assigned to the fact variable FactVariable_of_InterfaceType.
The predicate delegation has higher priority than the interface delegation. If to a predicate both delegations are specified. That is, the predicate delegation is specified to the predicate and it is declared in an interface, which has the interface delegation. Then the higher priority predicate delegation will be implemented.