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