Difference between revisions of "Language Reference/Facts"
m (header levels) |
(release) |
||
(8 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
{{languageReferenceNavbar|Facts}} | {{languageReferenceNavbar|Facts}} | ||
=== Facts Sections === | |||
A <vp>facts</vp> section declares a fact database, consisting of a number of facts. The fact database and the facts belong to the current scope. | A <vp>facts</vp> section declares a fact database, consisting of a number of facts. The fact database and the facts belong to the current scope. | ||
Line 26: | Line 28: | ||
<vipbnf><FactFunctorDeclaration> : | <vipbnf><FactFunctorDeclaration> : | ||
<FactName> : ( <Argument>-comma-sep-list-opt ) < | <FactName> : ( <Argument>-comma-sep-list-opt ) <FactMode>-opt</vipbnf> | ||
<vipbnf><FactName> : | <vipbnf><FactName> : | ||
Line 50: | Line 52: | ||
<vipbnf><InitialValue> : | <vipbnf><InitialValue> : | ||
:= < | := <Term> | ||
:= erroneous</vipbnf> | := erroneous</vipbnf> | ||
Line 56: | Line 58: | ||
<LowerCaseIdentifier></vipbnf> | <LowerCaseIdentifier></vipbnf> | ||
The initialization expression <vpbnf><InitialValue></vpbnf> must evaluate to a value of <vpbnf><Domain></vpbnf> type. | |||
The | The initialization expression can be omitted (only) if the fact variable is initialized in a constructor. Class fact variables should always have an initialization expression. | ||
The keyword <vp>erroneous</vp> can be used as value to be assigned to fact variables. That is both lines below are valid: | |||
<vip>facts | <vip>facts | ||
Line 68: | Line 70: | ||
The idea of assigning <vp>erroneous</vp> value is to give clear runtime error if some code uses uninitialized fact variable by mistake. | The idea of assigning <vp>erroneous</vp> value is to give clear runtime error if some code uses uninitialized fact variable by mistake. | ||
Visual Prolog has '''late''' initialization of fact variables, meaning that the initialization code for a fact variable is not execute before and unless it is needed. | |||
{{Example| | |||
Consider this code: | |||
<vip>facts | |||
current : integer := initializeCurrent(). | |||
</vip> | |||
The <vp>current</vp> is initialized by calling the function <vp>initializeCurrent</vp>. | |||
Initially nothing happens. If the first access to <vp>current</vp> is a '''read access''' to its value as in this code: | |||
<vip> | |||
... | |||
stdio::writef("Current = %\n", current), | |||
... | |||
</vip> | |||
then before the write takes place current will be initialized by evaluating its initialization expression (i.e. by calling <vp>initializeCurrent</vp>). | |||
If on the other hand the first access to <vp>current</vp> is a '''write access''' to its value as in this code: | |||
<vip> | |||
... | |||
current := 7, | |||
stdio::writef("Current = %\n", current), | |||
... | |||
</vip> | |||
then the initialization expression will never be evaluated, since the code never needed the value. | |||
The behavior of the code is very similar to this code: | |||
<vip>facts | |||
current_fact : integer := erroneous. | |||
properties | |||
current : integer. | |||
clauses | |||
current() = current_fact :- | |||
if isErroneous(current_fact) then | |||
current_fact := <initialize> | |||
end if. | |||
clauses | |||
current(P) :- | |||
current_fact := P.</vip> | |||
When the current property is read the fact will be initialized if it is currently <vp>erroneous</vp>. Or to put it differently: the initialization is done on a ''by-need'' basis. | |||
The main difference is that the fact variable is never erroneous when using the late fact initialization. | |||
}} | |||
If the initialization expression can be evaluated to a constant at compile time then the fact is initialized immediately, rather than '''late'''. | |||
Late initialization of facts is threadsafe in the following way: if two or more threads are reading a late fact simultaneously, they will/may potentially all execute the initialization code. But only '''''one''''' of the results will be used as the initial value, and all the threads will receive '''''that''''' value; all the other calculated initialization values will be discarded. | |||
The attribute <vp>immediate</vp> can be used to enforce immediate initialization of a fact variable. | |||
{{Example| | |||
This code will initialize <vp>current</vp> immediately: | |||
<vip>facts | |||
current : integer := initializeCurrent() [immediate]. | |||
</vip> | |||
}} | |||
=== Facts === | === Facts === | ||
Line 73: | Line 141: | ||
Facts can only be declared in a class implementation and subsequently they can only be referenced from this implementation. So the scope of facts is the implementation in which they are declared. But the lifetime of object facts is the lifetime of the object to which they belong. Likewise the lifetime of class facts are from program start to program termination. | Facts can only be declared in a class implementation and subsequently they can only be referenced from this implementation. So the scope of facts is the implementation in which they are declared. But the lifetime of object facts is the lifetime of the object to which they belong. Likewise the lifetime of class facts are from program start to program termination. | ||
{{Example| The following class declares an object fact <vp>objectFact</vp> and a class fact <vp>classFact</vp>: | |||
The following class declares an object fact <vp>objectFact</vp> and a class fact <vp>classFact</vp>: | |||
<vip>implement aaa_class | <vip>implement aaa_class | ||
Line 84: | Line 150: | ||
... | ... | ||
end implement aaa_class</vip> | end implement aaa_class</vip> | ||
}} | |||
=== Constant fact variable === | |||
A constant fact variable is a fact variable that never changes value after it has been initialized. It can for example be a global "table" that initialized at some point and then used for lookup afterwards. Or an "id" in an object identifying what the object represents. | |||
A fact variable is a constant fact variable if it is market with the attribute <vp>[constant]</vp>. | |||
A constant class fact variable can only be assigned: | |||
* Directly in the declaration | |||
* Or in a {{lang2|Attributes|classInitializer|classInitializer}} predicate | |||
A constant object fact variable can only be assigned: | |||
* Directly in the declaration | |||
* Or in a constructor | |||
{{Example|Declare and initialize a table. | |||
<vip> | |||
class facts | |||
translationTable : mapP{string, string} [constant]. | |||
class predicates | |||
initialize : () [classInitializer]. | |||
clauses | |||
initialize() :- | |||
% create the table | |||
translationTable := Table. | |||
</vip> | |||
}} | |||
{{Example|Make sure that the id property of the object is never changed (after creation): | |||
<vip> | |||
facts | |||
id : unsigned [constant]. | |||
clauses | |||
new(Id) :- | |||
id := Id. | |||
</vip> | |||
}} |
Latest revision as of 17:16, 12 March 2019
Facts Sections
A facts section declares a fact database, consisting of a number of facts. The fact database and the facts belong to the current scope.
Fact databases can exist on a class level as well as on an object level.
Facts sections can be declared only in class implementations.
If the fact database is named, an additional compound domain is implicitly defined. This domain has the same name as the fact section and has functors corresponding to the facts in the fact section.
If the facts section is named, the name denotes a value of the build-in domain factDB. The save and consult predicates accept values of this domain.
FactsSection : class-opt facts FactsSectionName-opt FactDeclaration-dot-term-list-opt
FactsSectionName : - LowerCaseIdentifier
Fact Declarations
A fact declaration declares a fact of a fact database. A fact declaration is either a fact variable, or a functor fact.
FactDeclaration : FactVariableDeclaration FactFunctorDeclaration
FactFunctorDeclaration : FactName : ( Argument-comma-sep-list-opt ) FactMode-opt
FactName : LowerCaseIdentifier
A fact functor declaration has nondeterm fact mode by default.
A fact functor can have initialization via clauses section. In such case values in the clauses should be expressions, which can be evaluated at compile time.
FactMode : one of determ nondeterm single
If mode is single, then a fact always has one and only one value and the assert predicate overwrites old value with a new one. Predicate retract cannot be applied to single facts.
If mode is nondeterm, then the fact can have zero, one, or any other number of values. If mode is determ, then the fact can have zero or one value. If fact has zero values, then any read access to it gives fail.
Fact Variable Declarations
A fact variable is similar to a one-argument single functor fact. However, syntactically it is used as a mutable variable (i.e. with assignment).
FactVariableDeclaration : FactVariableName : Domain InitialValue-opt
InitialValue : := Term := erroneous
FactVariableName : LowerCaseIdentifier
The initialization expression InitialValue must evaluate to a value of Domain type.
The initialization expression can be omitted (only) if the fact variable is initialized in a constructor. Class fact variables should always have an initialization expression.
The keyword erroneous can be used as value to be assigned to fact variables. That is both lines below are valid:
facts thisWin : vpiDomains::windowHandle := erroneous. clauses p() :- thisWin := erroneous.
The idea of assigning erroneous value is to give clear runtime error if some code uses uninitialized fact variable by mistake.
Visual Prolog has late initialization of fact variables, meaning that the initialization code for a fact variable is not execute before and unless it is needed.
Consider this code:
facts current : integer := initializeCurrent().
The current is initialized by calling the function initializeCurrent.
Initially nothing happens. If the first access to current is a read access to its value as in this code:
... stdio::writef("Current = %\n", current), ...
then before the write takes place current will be initialized by evaluating its initialization expression (i.e. by calling initializeCurrent).
If on the other hand the first access to current is a write access to its value as in this code:
... current := 7, stdio::writef("Current = %\n", current), ...
then the initialization expression will never be evaluated, since the code never needed the value.
The behavior of the code is very similar to this code:
facts current_fact : integer := erroneous. properties current : integer. clauses current() = current_fact :- if isErroneous(current_fact) then current_fact := <initialize> end if. clauses current(P) :- current_fact := P.
When the current property is read the fact will be initialized if it is currently erroneous. Or to put it differently: the initialization is done on a by-need basis.
The main difference is that the fact variable is never erroneous when using the late fact initialization.
If the initialization expression can be evaluated to a constant at compile time then the fact is initialized immediately, rather than late.
Late initialization of facts is threadsafe in the following way: if two or more threads are reading a late fact simultaneously, they will/may potentially all execute the initialization code. But only one of the results will be used as the initial value, and all the threads will receive that value; all the other calculated initialization values will be discarded.
The attribute immediate can be used to enforce immediate initialization of a fact variable.
This code will initialize current immediately:
facts current : integer := initializeCurrent() [immediate].
Facts
Facts can only be declared in a class implementation and subsequently they can only be referenced from this implementation. So the scope of facts is the implementation in which they are declared. But the lifetime of object facts is the lifetime of the object to which they belong. Likewise the lifetime of class facts are from program start to program termination.
implement aaa_class facts objectFact : (integer Value) determ. class facts classFact : (integer Value) determ. ... end implement aaa_class
Constant fact variable
A constant fact variable is a fact variable that never changes value after it has been initialized. It can for example be a global "table" that initialized at some point and then used for lookup afterwards. Or an "id" in an object identifying what the object represents.
A fact variable is a constant fact variable if it is market with the attribute [constant].
A constant class fact variable can only be assigned:
- Directly in the declaration
- Or in a classInitializer predicate
A constant object fact variable can only be assigned:
- Directly in the declaration
- Or in a constructor
class facts translationTable : mapP{string, string} [constant]. class predicates initialize : () [classInitializer]. clauses initialize() :- % create the table translationTable := Table.
facts id : unsigned [constant]. clauses new(Id) :- id := Id.