Difference between revisions of "Language Reference/Implementations"

From wiki.visual-prolog.com
(Private Constructors)
(Dynamic External Resolution)
 
(8 intermediate revisions by the same user not shown)
Line 16: Line 16:


<vipbnf><ClassImplementation> :
<vipbnf><ClassImplementation> :
  implement <ClassName> <ScopeQualifications> <Sections> end implement <ClassName>-opt</vipbnf>
    implement <ClassName>
        <ScopeQualifications>
    <Sections>
    end implement <ClassName>-opt</vipbnf>


See also {{lang|Generic Interfaces and Classes|Generic Interfaces and Classes}}.
See also {{lang|Generic Interfaces and Classes|Generic Interfaces and Classes}}.
Line 40: Line 43:
*{{lang2|Predicates|Predicates_Sections|PredicatesSection}}
*{{lang2|Predicates|Predicates_Sections|PredicatesSection}}
*{{lang2|Properties|Properties_Sections|PropertiesSection}}
*{{lang2|Properties|Properties_Sections|PropertiesSection}}
*{{lang2|Predicates|Predicates_Sections|PredicatesSection}}
*{{lang2|Facts|Facts_Sections|FactsSection}}
*{{lang2|Facts|Facts_Sections|FactsSection}}
*{{lang2|Clauses|Clauses_Section|ClausesSection}}
*{{lang2|Clauses|Clauses_Sections|ClausesSection}}
*{{lang2|Directives|Conditional_Compilation|ConditionalSection}}
*{{lang2|Directives|Conditional_Compilation|ConditionalSection}}


{{lang2|Predicates|Constructors_Sections|constructorsSection}}s are only legal if the class <vpbnf><ClassName></vpbnf> states a {{lang2|Classes|Class Declarations|constructionType}}. Classes that states a <vpbnf><ConstructionType></vpbnf> are also object constructors, which construct objects of the stated construction type.
{{lang2|Predicates|Constructors_Sections|constructorsSection}}s are only legal if the class <vpbnf><ClassName></vpbnf> states a {{lang|Classes|<vpbnf><ConstructionType></vpbnf>}}. Classes that states a <vpbnf><ConstructionType></vpbnf> are also object constructors, which construct objects of the stated construction type.


'''Example'''
'''Example'''
Line 254: Line 256:
   clauses
   clauses
       new() :-
       new() :-
           C = cc_class::new(),                   % create a cc_class object
           C = cc_class::new(), % create a cc_class object
           aa_class::newC(C).                   % invoke constructor on inherited sub-object
           aa_class::newC(C). % invoke constructor on inherited sub-object
       ...
       ...
end implement</vip>
end implement</vip>
Line 377: Line 379:
==== "This" and Inheritance ====
==== "This" and Inheritance ====


'''''[Notice''' this particular section is subject to change, as it is decided to change the language semantics in this matter.]''
Consider this code:
 
<vip>
interface iName
 
properties
    className : string (o).
 
end interface iName
 
%=========================


"<vp>This</vp>" always refer to an object belonging to the class in which "<vp>This</vp>" is used, also if that class is inherited by another class.
class aaa : iName
end class aaa


Assume the interface <vp>aa</vp> declared as follows:
%=========================


<vip>interface aa
implement aaa
  predicates
 
      action : ().
clauses
      doAction : ().
    className() = "aaa".
end interface</vip>
 
end implement aaa
 
%=========================


also assume the class <vp>aa_class</vp> declared as follows:
class bbb : iName
end class bbb


<vip>class aa_class : aa
%=========================
end class</vip>


And implemented as follows:
implement bbb inherits aaa
end implement aaa
</vip>


<vip>implement aa_class
We have to classes <vp>aaa</vp> and <vp>bbb</vp> that both implement the <vp>iName</vp> interface. <vp>bbb</vp> inherits <vp>aaa</vp>, so this code:
  clauses
      action() :-
          doAction(),
          This:doAction().
      doAction() :-
          write("aa_class::doAction"), nl.
end implement</vip>


The following goal:
<vip>
class predicates
    test : (string Class, iName Name).
clauses
    test(Class, Name) :-
        stdio::writef("% className = %\n", Class, Name:className).


<vip>goal
clauses
  A = aa_class::new(),
    run() :-
  A:action().</vip>
        test("aaa", aaa::new()),
        test("bbb", bbb::new()).
</vip>


Will write:
will output


<vip>aa_class::doAction
<pre>
aa_class::doAction</vip>
aaa className = aaa
bbb className = aaa
</pre>
I.e. <vp>bbb</vp> has inherited the implementation of thew <vp>className</vp> property from <vp>aaa</vp>.


Now also consider class <vp>bb_class</vp> declared as follows:
If we reimplement the className property in <vp>bbb</vp>:


<vip>class bb_class : aa
<vip>
end class</vip>
implement bbb inherits aaa


And implemented as follows
clauses
    className() = "bbb".


<vip>implement bb_class inherits aa_class
end implement aaa
  clauses
</vip>
      doAction() :-
          write("bb_class::doAction"), nl.
end implement</vip>


The following goal:
then the output will change to


<vip>goal
<pre>
  B = bb_class::new(),
aaa className = aaa
  B:action().</vip>
bbb className = bbb
</pre>


Will '''also '''write:
Because now bbb has its own implementation of the <vp>className</vp> property.


<vip>aa_class::doAction
Ley us extend the <vp>iName</vp> interface with a <vp>name</vp> property:
aa_class::doAction</vip>


because (both implicit and explicit) "<vp>This</vp>" in <vp>aa_class</vp> refers to the object of class <vp>aa_class</vp>.
<vip>
interface iName


Now consider else one example:
properties
    className : string (o).
    name : string (o).


<vip>interface iName
  predicates
      className : () -> string Class.
      name: () -> string Class.
      nameThis: () -> string Class.
end interface iName
end interface iName
</vip>


class aaa : iName
And implement it in <vp>aaa</vp> like this:
end class aaa


<vip>
implement aaa
implement aaa
  clauses
 
      className() = "aaa".
clauses
  clauses
    className() = "aaa".
      name() = className().
 
  clauses
clauses
      nameThis() = This:className().
    name() = className.
 
end implement aaa
end implement aaa
</vip>
We will not implement the <vp>name</vp> property in <vp>bbb</vp> so it will inherit the implementation from <vp>aaa</vp>.


class bbb : iName
Changing the test clause like this:
end class bbb
 
<vip>
clauses
    test(Class, Name) :-
        stdio::writef("% className = %\n", Class, Name:className),
        stdio::writef("% name = %\n", Class, Name:name).
</vip>
 
We will get this output:
 
<pre>
aaa className = aaa
aaa name = aaa
bbb className = bbb
bbb name = aaa
</pre>
 
Notice that the inherited name property in <vp>bbb</vp> returns <vp>"aaa"</vp>.
 
This is because the inherited <vp>name</vp> property references the <vp>aaa::className</vp>.
 
Let us perform a similar extension with a property <vp>nameThis</vp>:


implement bbb
<vip>
  inherits aaa
interface iName
  clauses
      className() = "bbb".
end implement bbb


goal
properties
  OOO = bbb::new(),
    className : string (o).
  Class1 = OOO:name(),
    name : string (o).
  Class2 = OOO:nameThis().</vip>
    nameThis : string (o).


Class <vp>bbb</vp> inherits the definition of <vp>name</vp> and <vp>nameThis</vp> from <vp>aaa</vp>, but it re-implements <vp>className</vp>. In the goal we create a <vp>bbb</vp> object and on this we call name and <vp>nameThis</vp>.
end interface iName
</vip>


The <vp>name</vp> predicate calls <vp>className</vp> directly, while <vp>nameThis</vp> calls <vp>This</vp>:<vp>className</vp>. The effects are different. The <vp>name</vp> predicate calls <vp>className</vp>, which is defined in <vp>aaa</vp>. But <vp>nameThis</vp> call the <vp>className</vp> that is defined on <vp>This</vp> and that is actually <vp>bbb::className</vp>, since <vp>This</vp> is a <vp>bbb</vp> object.
Again we will let bbb inherit the code from aaa, but this time the implementation in <vp>aaa</vp> will make an indirect reference to <vp>className</vp> through <vp>This</vp>:


All in all <vp>Class1</vp> is bound to the value <vp>"aaa"</vp>, whereas <vp>Class2</vp> is bound to <vp>"bbb"</vp>.
<vip>
implement aaa


Calling from a parent class to a child class like in <vp>nameThis</vp> 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.
clauses
    className() = "aaa".


Also consider this class:
clauses
    % Direct reference
    name() = className.


<vip>interface iTest
clauses
  predicates
    % Indirect reference through "This"
      test : () -> string Name.
    nameThis() = This:className.
end interface iTest


class ccc : iTest
end implement aaa
end class ccc
</vip>
   
implement ccc
  inherits aaa
  clauses
      test() = aaa::nameThis().
end implement ccc</vip>


<vp>ccc</vp> also inherits from <vp>aaa</vp>. Since <vp>ccc</vp> inherits from <vp>aaa</vp> it also implicitly supports the <vp>iName</vp> interface (i.e. privately). So when <vp>aaa</vp> calls <vp>This:className</vp> it call the one that <vp>ccc</vp> provides, which happens to be the one inherited from <vp>aaa</vp>.
Such an indirect reference through <vp>This</vp> behaves like calls made to objects from the "outside", so if updating the test in the "obvious" way:


Also consider this example:
<vip>
clauses
    test(Class, Name) :-
        stdio::writef("% className = %\n", Class, Name:className),
        stdio::writef("% name = %\n", Class, Name:name),
        stdio::writef("% name This= %\n", Class, Name:nameThis).
</vip>


<vip>class ddd : name
will produce this output:
end class ddd


implement ddd
<pre>
  inherits bbb
aaa className = aaa
  clauses
aaa name = aaa
      className() = "ddd".
aaa nameThis = aaa
end implement ddd
bbb className = bbb
goal
bbb name = aaa
  OOO = ddd::new(),
bbb nameThis = bbb
  Class1 = OOO:name(),
</pre>
  Class2 = OOO:nameThis().</vip>


<vp>ddd</vp> inherits from <vp>bbb</vp> which we know inherits from <vp>aaa</vp>. When calling <vp>nameThis</vp> we actually call <vp>aaa::nameThis</vp>, which calls <vp>This:className</vp>. In this case <vp>This</vp> is the <vp>ddd</vp> object and therefore the effective call will go to <vp>ddd::className</vp>.
Given an inheritance chain <vp>a5 inherits  a4 inherits  a3 inherits a2 inherits a1</vp>:


All in all a call to <vp>This:className</vp> goes to last child that supports an interface containing <vp>className</vp>.
* A ''direct'' reference in an implementation in <vp>a3</vp> to a predicate/property will refer to the entity as seen inside <vp>a3</vp>, which will therefore
**be implemented in <vp>a3</vp> or
**inherited from <vp>a2</vp> which may have inherited it from <vp>a1</vp>
* An ''indirect'' reference through <vp>This</vp> in an implementation in <vp>a3</vp> to a predicate/property will refer to the entity as see from the outside, and will therefore
**be implemented in <vp>a5</vp> or
**inherited from <vp>a4</vp> which may have inherited it from <vp>a3</vp>, ...


=== Inherits Qualification ===
=== Inherits Qualification ===
Line 559: Line 607:
       ...
       ...
end interface
end interface
class bb_class : aa
class bb_class : aa
end class
end class
class cc_class : aa
class cc_class : aa
end class
end class
class dd_class : aa
class dd_class : aa
end class
end class
implement dd_class inherits bb_class, cc_class
implement dd_class inherits bb_class, cc_class
end implement</vip>
end implement</vip>
Line 583: Line 635:


<vipbnf><Resolution> :
<vipbnf><Resolution> :
  <InterfaceResolution>
    <InterfaceResolution>
  <PredicateFromClassResolution>
    <PredicateFromClassResolution>
  <PredicateRenameResolution>
    <PredicateRenameResolution>
  <PredicateExternallyResolution></vipbnf>
    <PredicateExternallyResolution></vipbnf>


A resolve qualification is used to resolve an implementation from the specified source.
A resolve qualification is used to resolve an implementation from the specified source.
Line 593: Line 645:


<vipbnf><PredicateFromClassResolution> :
<vipbnf><PredicateFromClassResolution> :
  <PredicateNameWithArity> from <ClassName></vipbnf>
    <PredicateNameWithArity> from <ClassName></vipbnf>


A predicate from class resolution states that the predicate is implemented by the specified class.
A predicate from class resolution states that the predicate is implemented by the specified class.
Line 657: Line 709:
   <StringLiteral></vipbnf>
   <StringLiteral></vipbnf>


If the predicate {{lang2|Predicates|Arity|predicateNameWithArity}} is missed in the DLL <vpbnf><DllNameWithPath></vpbnf>, 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 <vpbnf><DllNameWithPath></vpbnf> 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 <vpbnf><DllNameWithPath></vpbnf> should be like <vp>"../DllName"</vp>. See also Dynamic Library Search Order in [http://msdn2.microsoft.com/en-us/library/ms682586.aspx MSDN].
If the predicate {{lang2|Predicates|Arity|predicateNameWithArity}} is not available in the DLL <vpbnf><DllNameWithPath></vpbnf>, then the dynamic loading provides the possibility to run a program until it actually invokes the predicate. A runtime error will occur on such invocation. The <vpbnf><DllNameWithPath></vpbnf> is the path to the DLL on the machine where the program should run, it can be absolute or relative. For example if the required dll is situated in the one level up from directory which the application loaded, then the <vpbnf><DllNameWithPath></vpbnf> should be like <vp>"../DllName"</vp>. See also [https://docs.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-search-order Dynamic-Link Library Search Order].


=== Finalization ===
=== Finalization ===
Line 755: Line 807:
   O_cc = cc_class::new(),
   O_cc = cc_class::new(),
   O_dd = dd_class::new(O_bb, O_cc),
   O_dd = dd_class::new(O_bb, O_cc),
   O_dd : p1(),                                   % This p1 from O_bb object
   O_dd : p1(), % This p1 from O_bb object
   O_dd : p2().                                   % This p2 from O_cc object</vip>
   O_dd : p2(). % This p2 from O_cc object</vip>


Actually in Visual Prolog delegation has the same effect as if you add clauses to the implementation of <vp>dd_class</vp> 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 <vp>dd_class</vp>:
Actually in Visual Prolog delegation has the same effect as if you add clauses to the implementation of <vp>dd_class</vp> 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 <vp>dd_class</vp>:

Latest revision as of 17:02, 23 December 2020

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.

Example

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.

Construction

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).

Default Constructor

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.

Example

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

Example

It is legal to implement the implicitly declared default constructor of aa_class:

implement aa_class
   clauses
       new() :-
          ...
end implement

Example

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

Example

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

Private Constructors

You also can declare "private" constructors in class implementations. This can be reasonable, for example, in the following situations:

  1. 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.
  2. 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
   ...

Sub-object Construction

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).

Example

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

Example 2

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.

Example

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.

Example

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.

"This"

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.

Explicit "This"

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.

Implicit "This"

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

Consider this code:

interface iName
 
properties
    className : string (o).
 
end interface iName
 
%=========================
 
class aaa : iName
end class aaa
 
%=========================
 
implement aaa
 
clauses
    className() = "aaa".
 
end implement aaa
 
%=========================
 
class bbb : iName
end class bbb
 
%=========================
 
implement bbb inherits aaa
end implement aaa

We have to classes aaa and bbb that both implement the iName interface. bbb inherits aaa, so this code:

class predicates
    test : (string Class, iName Name).
clauses
    test(Class, Name) :-
        stdio::writef("% className = %\n", Class, Name:className).
 
clauses
    run() :-
        test("aaa", aaa::new()),
        test("bbb", bbb::new()).

will output

aaa className = aaa
bbb className = aaa

I.e. bbb has inherited the implementation of thew className property from aaa.

If we reimplement the className property in bbb:

implement bbb inherits aaa
 
clauses
    className() = "bbb".
 
end implement aaa

then the output will change to

aaa className = aaa
bbb className = bbb

Because now bbb has its own implementation of the className property.

Ley us extend the iName interface with a name property:

interface iName
 
properties
    className : string (o).
    name : string (o).
 
end interface iName

And implement it in aaa like this:

implement aaa
 
clauses
    className() = "aaa".
 
clauses
    name() = className.
 
end implement aaa

We will not implement the name property in bbb so it will inherit the implementation from aaa.

Changing the test clause like this:

clauses
    test(Class, Name) :-
        stdio::writef("% className = %\n", Class, Name:className),
        stdio::writef("% name = %\n", Class, Name:name).

We will get this output:

aaa className = aaa
aaa name = aaa
bbb className = bbb
bbb name = aaa

Notice that the inherited name property in bbb returns "aaa".

This is because the inherited name property references the aaa::className.

Let us perform a similar extension with a property nameThis:

interface iName
 
properties
    className : string (o).
    name : string (o).
    nameThis : string (o).
 
end interface iName

Again we will let bbb inherit the code from aaa, but this time the implementation in aaa will make an indirect reference to className through This:

implement aaa
 
clauses
    className() = "aaa".
 
clauses
    % Direct reference
    name() = className.
 
clauses
    % Indirect reference through "This"
    nameThis() = This:className.
 
end implement aaa

Such an indirect reference through This behaves like calls made to objects from the "outside", so if updating the test in the "obvious" way:

clauses
    test(Class, Name) :-
        stdio::writef("% className = %\n", Class, Name:className),
        stdio::writef("% name = %\n", Class, Name:name),
        stdio::writef("% name This= %\n", Class, Name:nameThis).

will produce this output:

aaa className = aaa
aaa name = aaa
aaa nameThis = aaa
bbb className = bbb
bbb name = aaa
bbb nameThis = bbb

Given an inheritance chain a5 inherits  a4 inherits  a3 inherits  a2 inherits  a1:

  • A direct reference in an implementation in a3 to a predicate/property will refer to the entity as seen inside a3, which will therefore
    • be implemented in a3 or
    • inherited from a2 which may have inherited it from a1
  • An indirect reference through This in an implementation in a3 to a predicate/property will refer to the entity as see from the outside, and will therefore
    • be implemented in a5 or
    • inherited from a4 which may have inherited it from a3, ...

Inherits Qualification

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

Resolve Qualification

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.

Predicate Resolution

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

Interface Resolution

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.

Example

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

External Resolution

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 not available in the DLL DllNameWithPath, then the dynamic loading provides the possibility to run a program until it actually invokes the predicate. A runtime error will occur on such invocation. The DllNameWithPath is the path to the DLL on the machine where the program should run, it can be 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-Link Library Search Order.

Finalization

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).

Example

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

Delegate Qualification

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.

Predicate Delegation

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().

Interface Delegation

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.