Difference between revisions of "Language Reference/Implementations"
(Dynamic External Resolution) |
|||
(20 intermediate revisions by 2 users not shown) | |||
Line 5: | Line 5: | ||
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. | 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 | 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. <vp>predicates</vp> and <vp>facts</vp>) must be prefixed with the keyword <vp>class</vp>. All members declared in such sections are class members. | 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. <vp>predicates</vp> and <vp>facts</vp>) must be prefixed with the keyword <vp>class</vp>. All members declared in such sections are class members. | ||
Line 16: | Line 16: | ||
<vipbnf><ClassImplementation> : | <vipbnf><ClassImplementation> : | ||
implement <ClassName> | |||
<ScopeQualifications> | |||
<Sections> | |||
end implement <ClassName>-opt</vipbnf> | |||
See also {{lang|Generic Interfaces and Classes|Generic Interfaces and Classes}}. | |||
The <vpbnf><ClassName></vpbnf> in the end of the class implementation must (if present) be identical to the one in the beginning of the class implementation. | The <vpbnf><ClassName></vpbnf> in the end of the class implementation must (if present) be identical to the one in the beginning of the class implementation. | ||
Line 22: | Line 27: | ||
The <vpbnf><ScopeQualifications></vpbnf> must be of the kinds: | The <vpbnf><ScopeQualifications></vpbnf> must be of the kinds: | ||
*{{lang2|Interfaces|Supports_Qualification| | *{{lang2|Interfaces|Supports_Qualification|SupportsQualification}} | ||
*{{lang2|Interfaces|Open_Qualification| | *{{lang2|Interfaces|Open_Qualification|OpenQualification}} | ||
*{{lang2|Implementations|Inherits_Qualification| | *{{lang2|Implementations|Inherits_Qualification|InheritQualification}} | ||
*{{lang2|Implementations|Delegate_Qualification| | *{{lang2|Implementations|Delegate_Qualification|DelegateQualification}} | ||
A {{lang2|Interfaces|Supports_Qualification| | A {{lang2|Interfaces|Supports_Qualification|Supports qualification}} states the list of interfaces, which are supported privately by the class implementation. | ||
A {{lang2|Implementations|Delegate_Qualification| | A {{lang2|Implementations|Delegate_Qualification|Delegate qualification}} delegates functionality of (object) predicates from interfaces to predicates from objects, which can be stored as fact variables. | ||
The <vpbnf><Sections></vpbnf> must be of the kinds: | The <vpbnf><Sections></vpbnf> must be of the kinds: | ||
*{{lang2|Constants|Constants_Sections| | *{{lang2|Constants|Constants_Sections|ConstantsSection}} | ||
*{{lang2|Implementations|Resolve_Qualification| | *{{lang2|Implementations|Resolve_Qualification|ResolveQualification}} | ||
*{{lang2|Implementations|Delegate_Qualification| | *{{lang2|Implementations|Delegate_Qualification|DelegateQualification}} | ||
*{{lang2|Domains|Domains_Sections| | *{{lang2|Domains|Domains_Sections|DomainsSection}} | ||
*{{lang2|Predicates|Predicates_Sections| | *{{lang2|Predicates|Predicates_Sections|PredicatesSection}} | ||
*{{lang2| | *{{lang2|Properties|Properties_Sections|PropertiesSection}} | ||
*{{lang2|Facts|Facts_Sections| | *{{lang2|Facts|Facts_Sections|FactsSection}} | ||
*{{lang2|Clauses| | *{{lang2|Clauses|Clauses_Sections|ClausesSection}} | ||
*{{lang2|Directives|Conditional_Compilation| | *{{lang2|Directives|Conditional_Compilation|ConditionalSection}} | ||
{{lang2|Predicates|Constructors_Sections|constructorsSection}}s are only legal if the class <vpbnf><ClassName></vpbnf> states a {{ | {{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 52: | Line 57: | ||
<vip>interface aa | <vip>interface aa | ||
predicates | predicates | ||
setClassFact : (integer Value | setClassFact : (integer Value). | ||
getClassFact : () -> integer | getClassFact : () -> integer. | ||
setObjectFact : (integer Value | setObjectFact : (integer Value). | ||
getObjectFact : () -> integer | getObjectFact : () -> integer. | ||
end interface | end interface | ||
class aa_class : aa | class aa_class : aa | ||
Line 89: | Line 94: | ||
The class fact is shared among all objects, so setting the class fact via <vp>A1</vp> also affects the value obtained via <vp>A2</vp>. Hence, the value of <vp>ClassFact</vp> will be one, the value set via <vp>A1</vp>. | The class fact is shared among all objects, so setting the class fact via <vp>A1</vp> also affects the value obtained via <vp>A2</vp>. Hence, the value of <vp>ClassFact</vp> will be one, the value set via <vp>A1</vp>. | ||
On the other hand, object facts | On the other hand, object facts belong to each object. Therefore setting the object fact in <vp>A1</vp> will not affect the value stored in <vp>A2</vp>. Hence value of <vp>ObjectFact</vp> is zero, the value that the fact was initialized to in <vp>A2</vp>. | ||
== Construction == | === Construction === | ||
This section describes object construction and, as such, it only deals with classes that | This section describes object construction and, as such, it only deals with classes that produce objects. | ||
Objects are constructed by calling a ''constructor''. | Objects are constructed by calling a ''constructor''. | ||
Constructors are explicitly declared in <vp>constructors</vp> sections in class declarations and implementations | Constructors are explicitly declared in <vp>constructors</vp> sections in class declarations and implementations (see also {{lang2|Implementations|Default_Constructor|Default Constructor}}). | ||
A constructor actually has two associated predicates: | A constructor actually has two associated predicates: | ||
Line 108: | Line 113: | ||
The associated class function is defined implicitly, i.e. there are no clauses for it anywhere. | 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 | 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: | So before the clauses of the constructor are invoked: | ||
Line 124: | Line 129: | ||
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. | 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 | '''Note.''' During construction objects might not be valid and care must be taken not to access un-initialized parts of the object (see {{lang2|Implementations|Rules_for_Constructing_Objects|Rules for Constructing Objects}}). | ||
=== Default Constructor === | ==== Default Constructor ==== | ||
A ''default'' ''constructor'' is a null-ary constructor with the name <vp>new/0</vp>. If a class that constructs objects does not declare any constructors in the class declaration, then the default constructor (i.e. <vp>new/0</vp>) is implicitly declared (in the class declaration). This means that each class has at least one constructor. So writing: | A ''default'' ''constructor'' is a null-ary constructor with the name <vp>new/0</vp>. If a class that constructs objects does not declare any constructors in the class declaration, then the default constructor (i.e. <vp>new/0</vp>) is implicitly declared (in the class declaration). This means that each class has at least one constructor. So writing: | ||
Line 206: | Line 211: | ||
end class</vip> | end class</vip> | ||
=== Private Constructors === | ==== Private Constructors ==== | ||
You also can declare "private" constructors in class implementations. This can be reasonable, for example, in the following situations: | 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 | #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". | #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". | ||
Line 217: | Line 222: | ||
<vip>interface aa | <vip>interface aa | ||
end interface | end interface | ||
class aa : aa | class aa : aa | ||
end class | end class | ||
implement aa | implement aa | ||
constructors | constructors | ||
Line 226: | Line 233: | ||
... | ... | ||
end implement | end implement | ||
% program code | % program code | ||
... | ... | ||
Obj = aa::new(), % This is the declared | Obj = aa::new(), % This is the declared IMPLICIT default class constructor | ||
...</vip> | ...</vip> | ||
=== Sub-object Construction === | ==== 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. | 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. | ||
Line 248: | Line 256: | ||
clauses | clauses | ||
new() :- | new() :- | ||
C = cc_class::new(), | C = cc_class::new(), % create a cc_class object | ||
aa_class::newC(C). | aa_class::newC(C). % invoke constructor on inherited sub-object | ||
... | ... | ||
end implement</vip> | end implement</vip> | ||
Line 299: | Line 307: | ||
end implement bbb</vip> | end implement bbb</vip> | ||
=== Single (Object) Fact Initialization === | ==== 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. | 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. | ||
Line 324: | Line 332: | ||
end implement</vip> | end implement</vip> | ||
=== Construction by Delegation === | ==== 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. | 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. | ||
Line 344: | Line 352: | ||
end implement</vip> | end implement</vip> | ||
=== Rules for Constructing Objects === | ==== Rules for Constructing Objects ==== | ||
The programmer must ensure that: | The programmer must ensure that: | ||
Line 357: | Line 365: | ||
The compiler may offer to generate runtime validation, it may also offer to '''non-safely''' omit such runtime validations. | The compiler may offer to generate runtime validation, it may also offer to '''non-safely''' omit such runtime validations. | ||
== "This" == | === "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 "<vp>This</vp>". There are two kinds of access to "<vp>This</vp>" ''implicit'' and ''explicit''. | 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 "<vp>This</vp>". There are two kinds of access to "<vp>This</vp>" ''implicit'' and ''explicit''. | ||
=== Explicit "This" === | ==== Explicit "This" ==== | ||
In every clause of every object predicate the variable <vp>This</vp> is implicitly defined and bound to "<vp>This</vp>", i.e. the object whose member predicate is executing. | In every clause of every object predicate the variable <vp>This</vp> is implicitly defined and bound to "<vp>This</vp>", i.e. the object whose member predicate is executing. | ||
=== Implicit "This" === | ==== Implicit "This" ==== | ||
In the clause of an object member predicate other object member predicates can be called directly, because "<vp>This</vp>" 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 {{lang2|Basic_Concepts|Scoping_&_Visibility|Scoping & Visibility}}). Likewise the object facts (which are stored in "<vp>This</vp>") can be accessed. | In the clause of an object member predicate other object member predicates can be called directly, because "<vp>This</vp>" 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 {{lang2|Basic_Concepts|Scoping_&_Visibility|Scoping & Visibility}}). Likewise the object facts (which are stored in "<vp>This</vp>") can be accessed. | ||
=== "This" and Inheritance === | ==== "This" and Inheritance ==== | ||
Consider this code: | |||
<vip> | |||
interface iName | |||
properties | |||
className : string (o). | |||
end interface iName | |||
%========================= | |||
class aaa : iName | |||
end class aaa | |||
%========================= | |||
implement aaa | |||
clauses | |||
className() = "aaa". | |||
end implement aaa | |||
end | |||
%========================= | |||
class bbb : iName | |||
end class | end class bbb | ||
%========================= | |||
<vip> | implement bbb inherits aaa | ||
end implement aaa | |||
</vip> | |||
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: | |||
<vip> | |||
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()). | |||
</vip> | |||
will output | |||
< | <pre> | ||
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>. | |||
If we reimplement the className property in <vp>bbb</vp>: | |||
<vip> | <vip> | ||
implement bbb inherits aaa | |||
clauses | |||
className() = "bbb". | |||
end implement aaa | |||
</vip> | |||
then the output will change to | |||
< | <pre> | ||
aaa className = aaa | |||
bbb className = bbb | |||
</pre> | |||
Because now bbb has its own implementation of the <vp>className</vp> property. | |||
< | Ley us extend the <vp>iName</vp> interface with a <vp>name</vp> property: | ||
<vip> | |||
interface iName | |||
properties | |||
className : string (o). | |||
name : string (o). | |||
end interface iName | end interface iName | ||
</vip> | |||
And implement it in <vp>aaa</vp> like this: | |||
<vip> | |||
implement aaa | implement aaa | ||
clauses | |||
className() = "aaa". | |||
clauses | |||
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>. | |||
Changing the test clause like this: | |||
<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>: | |||
<vip> | |||
interface iName | |||
properties | |||
className : string (o). | |||
name : string (o). | |||
nameThis : string (o). | |||
end interface iName | |||
</vip> | |||
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>: | |||
<vip> | |||
implement aaa | |||
clauses | |||
className() = "aaa". | |||
clauses | |||
% Direct reference | |||
name() = className. | |||
clauses | |||
% Indirect reference through "This" | |||
nameThis() = This:className. | |||
end implement aaa | |||
end | </vip> | ||
implement | |||
<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: | ||
<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> | |||
will produce this output: | |||
<pre> | |||
aaa className = aaa | |||
aaa name = aaa | |||
aaa nameThis = aaa | |||
bbb className = bbb | |||
bbb name = aaa | |||
bbb nameThis = bbb | |||
</pre> | |||
<vp> | Given an inheritance chain <vp>a5 inherits a4 inherits a3 inherits a2 inherits a1</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 === | ||
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. | 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. | ||
Line 542: | Line 596: | ||
inherits <ClassName>-comma-sep-list</vipbnf> | inherits <ClassName>-comma-sep-list</vipbnf> | ||
== Resolve Qualification == | === Resolve Qualification === | ||
As mentioned elsewhere all ambiguities related to calling predicates can be avoided by using qualified names. | As mentioned elsewhere all ambiguities related to calling predicates can be avoided by using qualified names. | ||
Line 553: | 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 577: | Line 635: | ||
<vipbnf><Resolution> : | <vipbnf><Resolution> : | ||
<InterfaceResolution> | |||
<PredicateFromClassResolution> | |||
<PredicateRenameResolution> | |||
<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. | ||
=== Predicate Resolution === | ==== Predicate Resolution ==== | ||
<vipbnf><PredicateFromClassResolution> : | <vipbnf><PredicateFromClassResolution> : | ||
<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 596: | Line 654: | ||
*the class must be mentioned in the <vp>inherits</vp> section | *the class must be mentioned in the <vp>inherits</vp> section | ||
=== Predicate Rename Resolution === | ==== 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 | 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. | ||
<vipbnf><PredicateRenameResolution> : | <vipbnf><PredicateRenameResolution> : | ||
<PredicateNameWithArity> from <ClassName> :: <PredicateName></vipbnf> | <PredicateNameWithArity> from <ClassName> :: <PredicateName></vipbnf> | ||
=== Interface Resolution === | ==== 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. | 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. | ||
Line 629: | Line 687: | ||
end implement</vip> | end implement</vip> | ||
=== External Resolution === | ==== 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. | 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. | ||
Line 639: | Line 697: | ||
<vipbnf><PredicateExternallyResolution> : <PredicateNameWithArity> externally</vipbnf> | <vipbnf><PredicateExternallyResolution> : <PredicateNameWithArity> externally</vipbnf> | ||
=== Dynamic External Resolution === | ==== Dynamic External Resolution ==== | ||
A predicate externally resolution also provide syntax for dynamic loading of private and public class predicates from DLLs. | A predicate externally resolution also provide syntax for dynamic loading of private and public class predicates from DLLs. | ||
Line 651: | Line 709: | ||
<StringLiteral></vipbnf> | <StringLiteral></vipbnf> | ||
If the predicate {{lang2|Predicates|Arity|predicateNameWithArity}} is | 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 === | ||
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 | 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). | Classes can also implement a finalizer, which is a predicate that is invoked when the object is finalized (before it is removed from memory). | ||
Line 663: | Line 719: | ||
A finalizer is a procedure with no arguments and no return value, which has the name <vp>finalize</vp>. The predicate is implicitly declared and cannot be invoked directly from the program. | A finalizer is a procedure with no arguments and no return value, which has the name <vp>finalize</vp>. 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 | 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. | 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. | ||
Line 682: | Line 738: | ||
end implement aa_class</vip> | end implement aa_class</vip> | ||
== Delegate Qualification == | === Delegate Qualification === | ||
A <vp>delegate</vp> section contains a number of delegations: | A <vp>delegate</vp> section contains a number of delegations: | ||
Line 699: | Line 755: | ||
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. | 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 === | ==== Predicate Delegation ==== | ||
A object predicate delegation states that the predicate functionality is delegated to the predicate in the object specified with the fact variable <vpbnf><FactVariable_of_InterfaceType></vpbnf>. | A object predicate delegation states that the predicate functionality is delegated to the predicate in the object specified with the fact variable <vpbnf><FactVariable_of_InterfaceType></vpbnf>. | ||
Line 715: | Line 771: | ||
<vip>interface a | <vip>interface a | ||
predicates | predicates | ||
p1 : | p1 : (). | ||
p2 : | p2 : (). | ||
end interface | end interface | ||
Line 751: | 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(), | O_dd : p1(), % This p1 from O_bb object | ||
O_dd : p2(). | 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>: | ||
Line 759: | Line 815: | ||
p1() :- fv1:p1().</vip> | p1() :- fv1:p1().</vip> | ||
=== Interface Delegation === | ==== Interface Delegation ==== | ||
When you need to specify that functionality of all predicates declared in an interface <vpbnf><InterfaceName></vpbnf> are delegated to predicates from objects of the same inherited class, you can use the Interface Delegation specification: | When you need to specify that functionality of all predicates declared in an interface <vpbnf><InterfaceName></vpbnf> are delegated to predicates from objects of the same inherited class, you can use the Interface Delegation specification: | ||
Line 773: | Line 829: | ||
*The object supporting the interface must be constructed and be assigned to the fact variable <vpbnf><FactVariable_of_InterfaceType></vpbnf>. | *The object supporting the interface must be constructed and be assigned to the fact variable <vpbnf><FactVariable_of_InterfaceType></vpbnf>. | ||
The predicate delegation has higher priority | 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. |
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:
- ConstantsSection
- ResolveQualification
- DelegateQualification
- DomainsSection
- PredicatesSection
- PropertiesSection
- FactsSection
- ClausesSection
- ConditionalSection
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:
- 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 ...
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.