Language Reference/Facts

From wiki.visual-prolog.com

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.

Example

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.

Example

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.

Example The following class declares an object fact objectFact and a class fact classFact:
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:

A constant object fact variable can only be assigned:

  • Directly in the declaration
  • Or in a constructor
Example Declare and initialize a table.
class facts
  translationTable : mapP{string, string} [constant].
 
 class predicates
    initialize : () [classInitializer].
clauses
    initialize() :-
        % create the table
        translationTable := Table.
Example Make sure that the id property of the object is never changed (after creation):
facts
    id : unsigned [constant].
 
clauses
   new(Id) :-
      id := Id.