Professional Documents
Culture Documents
5
Language Reference
Prolog Development Center
www.visual-prolog.com
This document describes the syntax and semantics of the Visual Prolog programming language.
Visual Prolog is a strongly typed object oriented programming language based on the logical
programming language Prolog.
A Visual Prolog program consists of a goal and a number of:
l
l
l
Interfaces
Class declarations and
Class implementations
Domains
Constants
Predicates
Properties
Fact databases
The "actual" code of a Visual Prolog is the clauses, which are located in class implementations and
which implements predicates.
Language Reference
Page 1 / 167
t1 is an integral type whose values are the integers from 1 to 17 (both inclusive). Likewise, t2
contains the values from 5 to 13. So t2 is a subset of t1, but t2 is not a subtype of t1. On the
other hand, t3 (which contains the same values as t2) is a subtype of t1, because it is declared to
be so.
The language contains a few implicit subtype relations, but otherwise subtype relations are explicitly
stated in type definitions.
Object types are organized in a subtype hierarchy rooted in the predefined object type object, i.e.
any object type is a subtype of object. Object subtypes are defined by means of stating that one
interface supports another. If an object has an interface/object type that supports a certain other
interface, then the object also has that type and can without further interference be used as such
an object.
See also: Universal and Root Types
Object System
Language Reference
Page 2 / 167
objects
interfaces
classes
Object
An object is set of named object member predicates and a set of supported interfaces. Objects
actually also have a state, but this state can only be changed and observed through the member
predicates. We say that the state is encapsulated in the object.
Here encapsulation means the ability of an object to hide its internal data and methods, making only
the intended parts of the object programmatically accessible. The importance of encapsulation and
modularity is well known. Encapsulation helps building more structured and readable programs,
because objects are treated like black boxes. Look at a complex problem, find a part, which you can
declare and describe. Encapsulate it into an object, construct an interface and continue so, until you
have declared all the sub-problems. When you have encapsulated the objects of the problem, and
ensured, that they work correctly, you can abstract from them.
Interface
An interface is an object type. It has a name and defines a set of named object predicates.
Interfaces are structured in a supports hierarchy (the structure is a semi-lattice rooted in the
interface object). If an object has a type denoted by an interface, then it also has the type of any
supported interfaces. Therefore, the supports hierarchy is also a type hierarchy. An interface is a
subtype of all its supported interfaces. We also say that the object supports the interface. If the
interface is named X, then we say that the object is an X, or an X object.
Class
A class is a named object factory; it can create objects corresponding to a certain interface. Any
object is created by a class, if an object was created by the class, which uses the interface C to
construct objects then we call it a "C object".
All objects that are constructed by a certain class share the same definition of the object member
predicates, but each object has its own state. Thus, the object member predicates is actually part of
the class, whereas the state of the object is part of the object itself.
A class also contains another set of named predicates and an encapsulated state, known as the
class members and class state, respectively. The class members and the class state exist on a per
class basis, whereas the object members and object state exist on a per object basis. The class
state can be accessed both by class members and by object members.
Language Reference
Page 3 / 167
Language Reference
Page 4 / 167
l
l
Visual Prolog demands that names do not conflict at the point of declaration, because then it would
be impossible to solve the conflict at point of usage. Declarations can only conflict if they are in the
same scope, because qualification with scope can be used to solve the conflict. A name in one
category can never be in conflict with a name in another category, but as we shall see a single
declaration might place a name in several categories.
Packages
The basic units of code organization accepted in Visual Prolog are packages. We use packages in
Language Reference
Page 5 / 167
Local
Super-class & opened scopes
Opened scopes have the same status as super-classes, so in the sequel we will just say superclasses.
The hierarchy means that a local declaration will shadow a super-class declaration. But there is no
shadowing between super-classes; all super-classes have equal preference. If two or more super
classes contain conflicting declarations then these declarations can only be accessed by means of
qualification.
Example Assume the interface aa and the class aa_class:
interface aa
predicates
p1 : () procedure ().
p2 : () procedure ().
Language Reference
Page 6 / 167
Language Reference
Page 7 / 167
The lexical analysis of the program will break the compilation unit CompilationUnit into a list of input
elements InputElement
CompilationUnit:
InputElement-list
InputElement:
Comment
WhiteSpace
Token
Comments
A Visual Prolog comment is written in one of the following ways:
l
The /* (slash, asterisk) characters, followed by any sequence of characters (including new
lines), terminated by the */ (asterisk, slash) characters. These comments can be multi-lined.
They can also be nested.
The % (percent sign) character, followed by any sequence of characters. Comments that
begin with character % (percent sign) continue until the end of the line. Therefore, they are
commonly called single-line comments.
/* Begin of Comment1
%NestedComment2*/Comment1isnotterminated(single-line comment)
This is the real termination of Comment1 */
Whitespace
WhiteSpace:
Space
Tab
NewLine
Here Space is a space character, Tab is a tabulation character and NewLine is a new line character.
Tokens
Language Reference
Page 8 / 167
Identifiers
Identifier:
LowercaseIdentifier
UppercaseIdentifier
AnonymousIdentifier
Ellipsis
A LowercaseIdentifier is a sequence of letters, digits, and underscores that starts with a small
letter. An UppercaseIdentifier is a sequence of letters, digits, and underscores that starts either
with a capital letter or with an underscore.
AnonymousIdentifier :
Ellipsis :
...
Keywords
Keywords are divided into major and minor keywords, this division is only cosmetic however, there is
no formal difference between major and minor keywords. In the sequel we will however use different
coloring for them.
Keyword :
MajorKeyword
MinorKeyword
MajorKeyword : one of
class clauses constants constructors
delegate domains
end
facts
goal
implement inherits interface
monitor
namespace
open
predicates
properties
resolve
supports
MinorKeyword : one of
align and anyflow a s
bitsize
catch
determ digits div d o
Language Reference
Page 9 / 167
class
implement
interface
if
foreach
try
Punctuation Marks
Punctuation marks in Visual Prolog have syntactic and semantic meaning to the compiler, but do not
specify by themselves an operation that yields a value. Some punctuation marks, either alone or in
combinations, can also be Visual Prolog operators.
Punctuation marks are:
PunctuationMarks: one of
! , . # [ ] |
:-
::
Operators
Operators specify an evaluation to be performed on involved operands.
Operators: one of
^
/ * div mod quot rem
+ = < > <> >< <=
>=
:=
All operators are binary, but - and + also exist as unary operators.
div, mod, quot and rem are reserved words.
Literals
Literals fall into following categories: integer, character, floating-point, string, binary and list.
Language Reference
Page 10 / 167
Integral Literals
IntegerLiteral:
UnaryPlus-opt DecimalDigit-list
UnaryMinus-opt DecimalDigit-list
UnaryPlus-opt OctalPrefix OctalDigit-list
UnaryMinus-opt OctalPrefix OctalDigit-list
UnaryPlus-opt HexadecimalPrefix HexadecimalDigit-list
UnaryMinus-opt HexadecimalPrefix HexadecimalDigit-list
UnaryPlus:
+
UnaryMinus:
OctalPrefix:
0o
OctalDigit: one of
01234567
DecimalDigit: one of
0123456789
HexadecimalPrefix:
0x
HexadecimalDigit: one of
0123456789AaBbCcDdEeFf
An integral literal can belong to integer or unsigned domains and it should not exceed maximum and
minimum integer or unsigned values.
Real Literal
RealLiteral:
UnaryMinus-opt DecimalDigit-list FractionOfFloat-opt Exponent-opt
FractionOfFloat:
. DecimalDigit-list
Exponent:
ExponentSymbol ExponentSign-opt DecimalDigit-list
ExponentSymbol: one of
eE
ExponentSign: one of
-+
A floating literal should not exceed maximum and minimum real values.
Language Reference
Page 11 / 167
\\ representing \
\t representing Tab character
\n representing New Line character
\r representing carriage return
\' representing single quote
\" representing double quote
\uXXXX, here XXXX should be exactly four HexadecimalDigit's representing the Unicode
character corresponding to the digits.
String Literals
StringLiteral:
StringLiteralPart-list
StringLiteralPart:
' <CharacterValue>-list-opt '
" <CharacterValue>-list-opt "
@AtOpenChar AnyCharacter-list-opt AtCloseChar
\\ representing \
\t representing Tab character
\n representing New Line character
\r representing carriage return
\" representing double quote
\" representing single quote
\uXXXX, here XXXX should be exactly four HexadecimalDigit's representing the Unicode
character corresponding to the digits.
In single quoted strings it is optional to escape double quotes, and likewise it is optional to escape
single quotes in double quoted strings.
Single quoted strings must contain at least two characters otherwise they will be assumed to be a
character literal.
The @-literals can be used to avoid obscuring the string literals with escape characters. The literals
starts with @ followed by some non-letter character AtOpenChar. And it terminates when the close
character AtCloseChar is met. For most characters the close character is the same as the opening
character, but for diverse paranthesis charactes the close character is the corresponding opposite
paranthesis.
Open Close Open Close
Language Reference
Page 12 / 167
@)
@[
@]
@{
@}
@<
>
@>
<
For all non-paranthesis opening character the close character is the same as the open character, for
example @" is closed by ".
For all @-strings it is the case the twice the closing character does not close the string, but means
one occurence of the closing character in the string.
Example
This example uses @[ as opening and ] as closing, inside the string literal both " and ' can be used
unescaped:
Binary Literals
BinaryLiteral:
$[ ElementValue-comma-sep-list-opt ]
ElementValue:
IntegerLiteral
ElementValue should be any integral arithmetic expression (for example, constant), which should be
calculated while compilation-time and be in the range from 0 till 255.
List Literals
All elements in a list literal must belong to the same domain (or to compatible domains). This domain
can be any built-in or user defined domain, for example, it can be integral, character, binary,
compound domain, etc.
ListLiteral:
[ SimpleLiteral-comma-sep-list-opt ]
[ SimpleLiteral-comma-sep-list | ListLiteral ]
% empty list
Language Reference
Page 13 / 167
Example This is invalid because the elements in a list must all have same type:
[1,"abc"]
Language Reference
Page 14 / 167
A compilation item is an interface, a class declaration, a class implementation, the goal section or it
can be a conditional compilation item, which are described in Conditional Compilation.
CompilationItem :
Directive
NamespaceEntrance
ConditionalItem
InterfaceDefinition
ClassDeclaration
ClassImplementation
GoalSection
See also
l
l
l
l
l
l
l
Interfaces
Classes
Implementations
Namespaces
Goal Section
Directives
Conditional Item
Language Reference
Page 15 / 167
InterfaceName :
LowerCaseIdentifier
Supports Qualification
Open Qualification
ConstantsSection
DomainsSection
PredicatesSection
PredicatesFromInterface
PropertiesSection
ConditionalSection
All sections contained (transitively) in conditional sections must also be of those kinds.
Page 16 / 167
Supports Qualification
Supports qualifications can only be used in InterfaceDefinition and ClassImplementation.
Supports qualifications are used for two things:
l
specifying that one interface A extends another interface B and, thereby, that the object type
A is a subtype of the object type B
declaring that the objects of a certain class "privately" have more object types, than the one
specified as construction type.
Page 17 / 167
interface bbb
supports aaa
predicates
insert : (integer X, string Comment).
end interface
interface cc
supports aaa
predicates
extract : () -> integer.
end interface
interface dd
supports aaa, bbb, cc
predicates
extract : (string Comment) -> integer.
end interface
Some of the predicates are the same, so all in all, dd will contain the following members:
predicates
insert : (integer).
% origin interface: aaa
insert : (integer, string).
% origin interface: bbb
extract : () -> integer.
% origin interface: cc
extract : (string) -> integer.
% origin interface: dd
interface bbb
predicates
insert : (integer X).
Language Reference
Page 18 / 167
interface cc
supports aaa, bbb
end interface
% conflicting interfaces
The interface cc is illegal, because insert/1 supported in aaa has aaa as origin, whereas insert/1
supported in bbb has bbb as origin.
Language Reference
Page 19 / 167
ConstructionType :
: InterfaceName
ClassName :
LowerCaseIdentifier
Language Reference
Page 20 / 167
Notice that both the class and the interface can declare domains and constants and these must not
conflict with each other since they end up in the same name space (because they can be qualified
only with the same name of the interface or the class).
The ScopeQualifications must be of the kind OpenQualification.
The Sections must be of the kinds:
l
l
l
l
l
l
constantsSection
domainsSection
predicatesSection
constructorsSection
PropertiesSection
conditionalSection
Language Reference
Page 21 / 167
SupportsQualification
OpenQualification
InheritQualification
DelegateQualification
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:
l
ConstantsSection
Language Reference
Page 22 / 167
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 : a a
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
Language Reference
Page 23 / 167
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:
l
l
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:
l
l
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:
l
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 uninitialized parts of the object (see Rules for Constructing Objects).
Default Constructor
Language Reference
Page 24 / 167
Which is the same (negated) as: A class does not have a default constructor if:
l
l
Example
Given an interface aa, consider the following code:
class aa_class : a a
end class
Language Reference
Page 25 / 167
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 : a a
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 : a a
constructors
new : ().
newFromFile : (file File).
end class
% default constructor
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
Language Reference
Page 26 / 167
class aa : a a
end class
implement aa
constructors
myCreate : ().
clauses
myCreate() :...
end implement
% program code
...
Obj = aa::n e w(),
% 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::n e w(), % 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:
Language Reference
Page 27 / 167
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
Language Reference
Page 28 / 167
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::n e w(),
newFromC(C).
newFromC(C) :assert(c(C)).
...
end implement
There is no guarantee about how clever the compiler is in detecting such problems at compile time.
Language Reference
Page 29 / 167
"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
[Notice this particular section is subject to change, as it is decided to change the language
semantics in this matter.]
"This" always refer to an object belonging to the class in which "This" is used, also if that class is
inherited by another class.
Assume the interface aa declared as follows:
interface aa
predicates
action : ().
doAction : ().
end interface
Page 30 / 167
Will write:
aa_class::doAction
aa_class::doAction
because (both implicit and explicit) "This" in aa_class refers to the object of class aa_class.
Now consider else one example:
interface iName
predicates
className : () -> string Class.
name: () -> string Class.
nameThis: () -> string Class.
end interface iName
implement aaa
Language Reference
Page 31 / 167
implement bbb
inherits aaa
clauses
className() = "bbb".
end implement bbb
goal
OOO = bbb::n e w(),
Class1 = OOO:name(),
Class2 = OOO:nameThis().
Class bbb inherits the definition of name and nameThis from aaa, but it re-implements className.
In the goal we create a bbb object and on this we call name and nameThis.
The name predicate calls className directly, while nameThis calls This:className. The effects
are different. The name predicate calls className, which is defined in aaa. But nameThis call the
className that is defined on This and that is actually bbb::className, since This is a bbb object.
All in all Class1 is bound to the value "aaa", whereas Class2 is bound to "bbb".
Calling from a parent class to a child class like in nameThis 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.
Also consider this class:
interface iTest
predicates
test : () -> string Name.
end interface iTest
implement ccc
inherits aaa
clauses
test() = aaa::nameThis().
end implement ccc
ccc also inherits from aaa. Since ccc inherits from aaa it also implicitly supports the iName interface
(i.e. privately). So when aaa calls This:className it call the one that ccc provides, which happens
to be the one inherited from aaa.
Also consider this example:
class ddd : name
end class ddd
Language Reference
Page 32 / 167
ddd inherits from bbb which we know inherits from aaa. When calling nameThis we actually call
aaa::nameThis, which calls This:className. In this case This is the ddd object and therefore the
effective call will go to ddd::className.
All in all a call to This:className goes to last child that supports an interface containing
className.
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
Language Reference
Page 33 / 167
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 : a a
end class
class cc_class : a a
end class
class dd_class : a a
end class
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 :
Language Reference
Page 34 / 167
Resolution :
InterfaceResolution
PredicateFromClassResolution
PredicateRenameResolution
PredicateExternallyResolution
A predicate from class resolution states that the predicate is implemented by the specified class.
To resolve a predicate to a class:
l
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
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
Language Reference
Page 35 / 167
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
DllNameWithPath :
StringLiteral
If the predicate predicateNameWithArity is missed in the DLL DllNameWithPath, 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 DllNameWithPath 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 DllNameWithPath
should be like "../DllName". See also Dynamic Library Search Order in MSDN.
Finalization
Language Reference
Page 36 / 167
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.
Language Reference
Page 37 / 167
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.
interface aa
supports a
end interface
class bb_class : a
end class
class cc_class : a
end class
class dd_class : a a
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,
Language Reference
Page 38 / 167
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:
l
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.
Language Reference
Page 39 / 167
Interfaces
Classes
Implementations
The pragmatic reason to use generic classes and interfaces is to declare parametrized object facts
and implement operations on these facts. As illustrated in the Queue example below.
Example: Queue
Consider this interface
interface queue_integer
predicates
insert : (integer Value).
tryGet : () -> integer Value.
end interface queue_integer
An object of this type is a queue of integers, if you replace "integer" with "string" you would have a
type describing queues of strings.
A generic interface can be used to describe all such interfaces in a single interface definition:
interface queue{@Elem}
predicates
insert : (@Elem Value).
tryGet : () -> @Elem Value determ.
end interface queue
@Elem is a scope type variable (distinguished from local type variables by the @).
queue{integer} represents integer-queues; queue{string} represents string-queues; and so forth.
We can declare a generic queue class like this:
class queueClass{@Elem} : queue{@Elem}
end class queueClass
Page 40 / 167
This piece of code illustrates how to create an integer queue and insert an element in it:
..., Q = queueClass{integer}::n e w(), Q:insert(17), ...
It is not necessary to apply the type explicitly, instead the compiler can infer it from the context:
..., Q = queueClass::n e w(), Q:insert(17), ...
The compiler sees that Q must be an integer queue, because we insert 17 into it.
Generic Interfaces
Syntax
Generic interfaces have a list of type parameters:
InterfaceDeclaration :
interface InterfaceName { ScopeTypeVariable-comma-sep-list-opt }
ScopeQualifications
Sections
end interface InterfaceName-opt
ScopeTypeVariable :
@ UppercaseName
The scope type parameters can be used in any declaration/definition in the interface.
Semantics
A generic interface defines all the interfaces that can be obtained by instantiating the type
parameters with actual types. The scope type parameters from the opening are bound in the entire
interface.
Restrictions
Then closing name should not have parameters:
interface xxx{@A}
...
end interface xxx % no parameters here
It is illegal to use same interface name for interfaces with different arity (in the same namespace):
Language Reference
Page 41 / 167
interface xxx{@A, @B} % error: Several classes, interfaces and/or namespaces have the same name 'xxx'
...
end interface xxx
Supported interfaces can be instantiated with any type expressions (as long as parameters are
bound):
interface xxx{@A} supports yyy{integer, @A*}
...
Generic Classes
Syntax
Generic classes have a list of type parameters, and constructs objects of an interface type uses the
these parameters.
ClassDeclaration :
class ClassName { ScopeTypeVariable-comma-sep-list-opt } ConstructionType
ScopeQualifications
Sections
end class ClassName-opt
ScopeTypeVariable :
@ UppercaseName
ConstructionType :
: TypeExpression
Language Reference
Page 42 / 167
It is illegal to use same class name for class with different arity (in the same namespace):
class xxx % xxx/0
...
end class xxx
class xxx{@A} : yyy{@A} % error: Several classes, interfaces and/or namespaces have the same name 'xxx'
...
end class xxx
If a class and interface can have the same name, the class must construct objects of that interface.
interface xxx{@A}
...
end interface xxx
class xxx{@Q, @P} : object % error: Several classes, interfaces and/or namespaces have the same name 'xxx'
...
end class xxx
All the parameters from the class must be used in the construction type:
class xxx{@P} : object % error: Unused type parameter '@P'
...
In class declarations scope parameter can only be used in constructors, domains and constants.
class xxx{@P} : xxx{@P}
domains
list = @P*. % legal
constants
empty : @P* = []. % legal
constructors
new : (@P Init). % legal
predicates
p : (@P X). % error: Scope parameter '@P' used in a class entity
end class xxx
Generic Implmentations
Syntax
Generic implementations have a list of type parameters.
Language Reference
Page 43 / 167
ScopeTypeVariable :
@ UppercaseName
Semantics
A generic class declares a class with a generic constructor. The type of the constructed object will
be inferred from the usage of the constructor.
Restrictions
Then closing name should not have parameters:
implement xxx{@A}
...
end implement xxx % no parameters here
The parameters must be the same as in the corresponding class declaration and have same order.
class xxx{@A} : aaa{@A}
...
end class xxx
implement xxx{@B} % error: The parameter '@B' is not the same as in the declaration '@A'
...
end implement xxx
In class implementations scope parameter can be used in constructors, domains, constants and in
object entities (i.e. object facts, object predicates and object properties).
implement xxx{@P}
domains
list = @P*. % legal
constants
empty : @P* = []. % legal
constructors
new : (@P Init). % legal
predicates
op : (@P X). % legal
class predicates
cp : (@P X). % error: Scope parameter '@P' used in a class entity
facts
ofct : (@P). % legal
class facts
cfct : (@P). % error: Scope parameter '@P' used in a class entity
...
end implement xxx
Language Reference
Page 44 / 167
Syntax
Monitor interfaces and monitor classes are scopes:
Scope : one of
...
MonitorInterface
MonitorClass
...
A monitor interface is defined by writing the keyword monitor in front of a regular interface
definition:
MonitorInterface :
monitor IntertfaceDefinition
A monitor class is declared by writing the keyword monitor in front of a regular class declaration:
MonitorClass :
monitor ClassDeclaration
Monitor classes and interfaces cannot declare multi and nondeterm predicate members.
Restrictions
l
l
l
Semantics
The predicates and properties declared in a monitor are the entrances to the monitor. A thread
enters the monitor through an entrance and is in the monitor until it leaves that entrance again. Only
one thread is allowed to be in the monitor at the time. So each entry is protected as a critical region.
The semantics is simplest to understand as a program transformation (which is how it is
implemented). Consider this academic example:
monitor class mmmm
predicates
e1 : (a1 A1).
e2 : (a2 A2).
Language Reference
Page 45 / 167
%----------------------------------------implement mmmm
clauses
e1(A1) :- <B1>.
clauses
e2(A2) :- <B2>.
...
clauses
en(An) :- <Bn>.
end implement mmmm
Where <B1>, <B2>, ..., <Bn> are clause bodies. This code corresponds to the following "normal"
code:
class mmmm
predicates
e1 : (a1 A1).
e2 : (a2 A2).
...
en : (an An).
end class mmmm
%----------------------------------------implement mmmm
class facts
monitorRegion : mutex := mutex::create(false).
clauses
e1(A1) :_ W = monitorRegion:wait(),
try
<B1>
finally
monitorRegion:release()
end try.
clauses
e2(A2) :_ W = monitorRegion:wait(),
try
<B2>
finally
monitorRegion:release()
end try.
...
clauses
en(An) :_ W = monitorRegion:wait(),
try
<Bn>
finally
monitorRegion:release()
end try.
end implement mmmm
So each monitor class is extended with a mutex, which is used to create a critical region around each
entry body.
The code for monitor objects is similar, except that the mutex object is owned by the object.
Language Reference
Page 46 / 167
GuardClause : one of
LowerCaseIdentifier guard LowerCaseIdentifier .
LowerCaseIdentifier guard AnonymousPredicate .
The guard predicates are evaluated when the monitor is created. For monitor classes this means at
program start, for object predicates this is immediately after the construction of the object. The
guard predicates are also evaluated whenever a tread leaves the monitor. But they are not evaluated
at any other time.
If a certain guard succeeds the corresponding entry is open, if it fails the entry is closed.
It is only possible to enter open entries.
Example Here is a queue class that solves the pick-out problem using a guard predicate on the
remove operation:
monitor class queue
predicates
insert : (integer Element).
Language Reference
Page 47 / 167
%----------------------------------------implement queue
class facts
element_fact : (integer Element) nondeterm.
clauses
insert(Element) :assert(element_fact(Element)).
clauses
remove guard remove_guard.
remove() = Element :retract(element_fact(Element)),
!;
exception::raise_error("The guard should have ensured that the queue is not empty").
predicates
remove_quard : () determ.
clauses
remove_guard() :element_fact(_),
!.
end implement queue
Notice that remove is a procedure, because threads that call remove will wait until there is an
element for them. The guard predicate remove_guard succeeds if there is an element in the queue.
So remove_guard is evaluated each time a thread leaves the monitor, and the element_fact fact
database can only be changed by a thread that is inside the monitor. Therefore the guard value
stays sensible all the time (i.e. when there are no threads in the monitor). It is important to ensure
such "stays sensible" condition for guards.
Guard predicates are handled in the transformation mentioned above.
Example The queue example is effectively the same as this "monitor-free" code:
class queue
predicates
insert : (integer Element).
predicates
remove : () -> integer Element.
end class queue
%----------------------------------------implement queue
class facts
monitorRegion : mutex := mutex::create(false).
remove_guard_event : event := event::create(true, toBoolean(remove_guard())).
element_fact : (integer Element) nondeterm.
clauses
insert(Element) :_ W = monitorRegion:wait(),
try
assert(element_fact(Element))
finally
setGuardEvents(),
monitorRegion:release()
end try.
clauses
Language Reference
Page 48 / 167
class predicates
remove_guard : () determ.
clauses
remove_guard() :element_fact(_),
!.
class predicates
setGuardEvents : ().
clauses
setGuardEvents() :remove_guard_event:setSignaled(toBoolean(remove_guard())).
end implement queue
An event is created for each guard predicate; this event is set to signaled if the guard predicate
succeeds. As mentioned it is set during the creation of the monitor and each time a predicate leaves
the monitor (before it leaves the critical region).
When entering an entry the threads waits both for the monitorRegion and for the guard event to
be in signalled state.
In the code above the initialization of the class itself and the guard events are done in an
undetermined order. But actually it is ensured that the guard events are initialized after all other
class/object initialization is performed.
%----------------------------------------implement log
class facts
logStream : outputStream := erroneous.
clauses
write(...) :logStream:write(time::n e w():formatShortDate(), ": "),
logStream:write(...),
logStream:nl().
end implement log
Language Reference
Page 49 / 167
%----------------------------------------implement outputStream_sync
delegate interface outputStream to stream
facts
stream : outputStream.
clauses
new(Stream) :- stream := Stream.
end implement outputStream_sync
consists of three separate operations, so it can still be the case (fx) that two threads first write
the time and then one writes the "...", etc.
Queue
The queue above is fine, but actually it may be better to create queue objects. Using generic
interfaces we can create a very general queue:
monitor interface queue{@Elem}
predicates
enqueue : (@Elem Value).
predicates
dequeue : () -> @Elem Value.
end interface queue
%----------------------------------------implement queue{@Elem}
facts
Language Reference
Page 50 / 167
clauses
enqueue(V) :assert(value_fact(V)).
clauses
dequeue guard { value_fact(_), ! }.
dequeue() = V :retract(value_fact(V)),
!.
dequeue() = V :common_exception::raise_error(....).
end implement queue
References
l
wikipedia:Monitor (synchronization)
Language Reference
Page 51 / 167
NamespaceIdentifier: one of
LowercaseIdentifier
LowercaseIdentifier \ NamespaceIdentifier
Namespace entrances in an #include-file does not change the namespace region in the
including file
Any file starts in the root namespace (also if it is included inside a namespace region in another
file).
Any interface, class and implementation that is meet inside a namespace region belongs to that
namespace.
Example
class aaa
end class aaa
namespace xxx
class bbb
end class bbb
namespace xxx\yyy
class ccc
end class ccc
This file is divided in three regions (assuming that it is a complete file). The first region is in the root
namespace (\), the second region belongs to the xxx namespace and the third region belongs to the
Language Reference
Page 52 / 167
OpenQualification: one of
open NamespaceIdentifier\
...
Language Reference
Page 53 / 167
Language Reference
Page 54 / 167
Domain Definitions
A domain definition defines a named domain in the current scope.
DomainDefinition:
DomainName FormalTypeParameterList-opt = DomainExpression
If the domain on the right hand side denotes an interface or a compound domain, then the defined
domain is synonym (i.e. identical) to the type expression. Otherwise the defined domain becomes a
subtype of the domain denoted by the type expression. Here a domain name DomainName should be
a lower case identifier.
There are certain places where you must use a domain name rather than a type expression:
l
l
l
Domain Expressions
A domain expression denotes a type in a domain definition:
DomainExpression:
TypeName
CompoundDomain
ListDomain
PredicateDomain
IntegralDomain
RealDomain
TypeVariable
ScopeTypeVariable
TypeApplication
Type Expressions
The full range of DomainExpressions can only be used in a domain definition. TypeExpression is a
subset of these expressions that are used in other many other contexts.
TypeExpression:
TypeName
ListDomain
TypeVariable
ScopeTypeVariable
Language Reference
Page 55 / 167
Type Names
A type name is either an interface name or the name of a value domain. We use the term value
domain to specify domains whose elements are immutable (unchangeable). Here we can say that
objects, belonging to domains correspondent to interface names, have mutable state and terms of
any other domains are immutable. So actually value types are everything except object types. A type
name (obviously) denotes the type corresponding to the name of an existing domain.
TypeName:
InterfaceName
DomainName
ClassQualifiedDomainName
InterfaceName:
LowercaseIdentifier
DomainName:
LowercaseIdentifier
ClassQualifiedDomainName:
ClassName::DomainName
ClassName:
LowercaseIdentifier
Here InterfaceName is an interface name, DomainName is a value domain name, and ClassName
is a class name.
Example
domains
newDomain1 = existingDomain.
newDomain2 = myInterface.
In this example the domain name existingDomain and the interface name myInterface are used to
define new domains.
Compound Domains
Compound domains (also known as algebraic data types) are used to represent lists, trees, and other
tree structured values. In its simple forms compound domains are used to represent structures and
enumeration values. Compound domains can have a recursive definition. They can also be
mutually/indirectly recursive.
CompoundDomain:
Alignment-opt FunctorAlternative-semicolon-sep-list
Language Reference
Page 56 / 167
Alignment:
align IntegralConstantExpression
Here FunctorName is the name of a functor alternative it should be a lower case identifier.
FormalArgument:
TypeExpression ArgumentName-opt
Here ArgumentName can be any upper case identifier. The compiler ignores it.
Compound domains have no subtype relations to any other domains.
If a domain is defined as being equal to a compound domain, then these two domains are synonym
types rather than subtypes. Meaning that they are just two different names for the same type.
Example
domains
t1 = ff(); gg(integer, t1).
t1 is a compound domain with two alternatives. The first alternative is the null-ary functor ff, while
the second alternative is the two-ary functor gg, which takes an integer and a term of the domain t1
itself as arguments. So the domain t1 is recursively defined.
The following expressions are terms of the domain t1:
ff()
gg(77, ff())
gg(33, gg(44, gg(55, ff())))
Example
domains
t1 = ff(); gg(t2).
t2 = hh(t1, t1).
Language Reference
Page 57 / 167
Example
In this example t1 and t2 are synonym types.
domains
t1 = f(); g(integer).
t2 = t1.
Normally, it is not necessary to use empty parenthesis after null-ary functors. But in a domain
definition consisting only of a single null-ary functor, empty parenthesis are required to distinguish it
from a synonym/subtype definition.
Example
t1 is a compound domain with a single null-ary functor, whereas t2 is defined to be synonym to t1.
domains
t1 = f().
t2 = t1.
List Domains
List domains represent sequences of values of a certain domain. Thus, all elements in a T list must be
of type T.
ListDomain:
TypeExpression *
Tail:
Language Reference
Page 58 / 167
Here Tail is a term which should have a value of the ListDomain type. Each Term should be of
typeName type.
Actually, lists are just compound domains with two functors: [] denoting the empty list and the mixfix functor [HD|TL] denoting the list with head HD and tail TL. The head must be of the underlying
element type, whereas the tail must be a list of relevant type.
Lists are however syntactically sugared.
[E1, E2, E3, ..., En | L ] is shorthand for [E1 |[ E2 |[ ...[ En | L ]...}}]
[E1, E2, E3, ..., En] is shorthand for [E1, E2, E3, ..., En |[}}, which in turn is shorthand for [E1 |
[ E2 |[ ...[ En | [] ]...}}]
Predicate Domains
Values of a predicate domain are predicates with the same "signature", i.e. the same argument and
return types, the same flow pattern and the same (or stronger) predicate mode.
A predicate that returns a value is called a function, whereas a predicate that does not return a
value is sometimes called an ordinary predicate, to stress that it is not a function.
PredicateDomain:
( FormalArgument-comma-sep-list-opt ) ReturnArgument-opt
PredicateModeAndFlow-list-opt CallingConvention-opt
FormalArgument:
TypeExpression VariableName-opt
Ellipsis
ReturnArgument:
-> FormalArgument
VariableName:
UpperCaseIdentifier
Predicate domains can have Ellipsis argument as the last FormalArgument in the
FormalArgument-comma-sep-list.
Predicate domains can have AnonymousIdentifier as a predicateArgumentType to specify that the
argument can be of any type.
Currently, predicate domains with ellipsis can only be used in predicate declarations.
Predicate domains that are used in domain definitions can at most state one flow.
PredicateModeAndFlow:
PredicateMode-opt
Language Reference
Page 59 / 167
Predicate Mode
The specified predicate mode applies for each member of a flow pattern list following it.
PredicateMode: one of
erroneous
failure
procedure
determ
multi
nondeterm
If Fail is in the set it means that the predicate can fail. If succeed is in the set it means that the
predicate can succeed. If BacktrackPoint is in the set it means that the predicate can return with an
active backtrack point in it.
If such a set, say failure, is a subset of another set, say nondeterm, then we say that the mode is
stronger than the other, i.e. failure is stronger than nondeterm.
A predicate domain actually contain all predicates (with correct type and flow), which have the mode
specified or a stronger mode.
It is illegal to state a predicate mode for constructors, they always have the procedure mode.
Omitting of a predicate mode means procedure.
Flow Pattern
The flow pattern defines the input/output direction of the arguments, which in combination with
functor domains can be structures with parts of a single argument being input and other parts of the
same argument being output.
A flow pattern consists of a sequence of flows, each flow corresponds to an argument (fist flow to
first argument, etc).
FlowPattern:
( Flow-comma-sep-list-opt ) AnyFlow
Flow: one of
i
Language Reference
Page 60 / 167
Ellipsis flow must match an ellipsis argument and can therefore be only the last flow in the flow
pattern.
Ellipsis:
...
A functor flow FunctorFlow states a functor and flows of each of the components of that flow. The
functor must of course be in the domain of the corresponding argument.
FunctorFlow:
FunctorName ( Flow-comma-sep-list-opt )
ListFlowTail:
| Flow
pp1 is a predicate domain. The predicates that have type pp1 takes one integer argument. Since no
flow-pattern is stated the argument is input, and since no predicate mode is mentioned the
predicates are procedure.
Example
domains
Language Reference
Page 61 / 167
Predicates of type pp2 take one integer argument and returns a value of type integer. Therefore,
pp2 is actually a function domain and the predicates that have type pp2 are actually functions.
Since no flow-pattern is stated the argument is input and since no predicate mode is mentioned the
predicates are procedure.
Example
predicates
ppp : (integer Argument1, integer Argument2) determ (o,i) (i,o) nondeterm (o,o).
The predicate ppp takes two integer arguments. It exists in three flow variants: (o,i) and (i,o),
which are determ, and (o,o), which is nondeterm.
Calling Convention
The calling convention determines how arguments, etc. are passed to the predicate, it also
determines how the link name is derived from a predicate name.
CallingConvention:
language CallingConventionKind
CallingConventionKind: one of
c thiscall stdcall apicall prolog
If a calling convention is not stated, then the prolog convention is assumed. The prolog calling
convention is the standard convention used for Prolog predicates.
The calling convention c follows the C/C++ standard calling convention. The link name of a predicate
is created from the predicate name by adding a leading underscore (_).
The calling convention thiscall follows the C++ standard calling convention for virtual functions. This
calling convention uses the c link name strategy but sometimes it may use the different argument and
stack handling rules. Calling convention thiscall can be applied to the object predicates only.
The calling convention stdcall uses the c link name strategy but it uses the different argument and
stack handling rules. The following table shows the implementation of stdcall calling convention:
Feature
Implementation
Argument-passing
order
Right to left.
Argument-passing
convention
Stack-maintenance
responsibility
Name-decoration
convention
Language Reference
Page 62 / 167
The calling convention apicall uses the same argument and stack handling rules as stdcall, but for
convenience to call MS Windows API functions apicall uses the naming conventions that are used by
most MS Windows API functions. According to apicall naming conventions the link name of a
predicate is constructed as follows:
l
l
l
l
l
Example
predicates
predicateName : (integer, string) language apicall
The argument types of this predicate indicates that it is a Unicode predicate (as string is the domain
of Unicode strings). An integer and a string each occupies 4 bytes on the call stack and, therefore,
the link name becomes:
_PredicateNameW@8
If apicall is used together with the "as" construction the name stated in the "as" construction is
decorated in the same manner.
apicall can only be used directly in a predicate declaration, not in a predicate domain definition. In
predicate domain definitions stdcall, must be used instead. A predicate declared with apicall calling
convention cannot have clauses and it also cannot be resolved externally without explicit DLL name.
The following table compares implementations of c, apicall, and stdcall calling conventions (the
prolog calling convention has the special implementation, which is not discussed here):
Keyword
Stack cleanup
Predicate name
case-translation
None.
thiscall
None.
stdcall
Language Reference
Page 63 / 167
Visual Prolog notion of predicate domains covers both class and object members. Class members are
handled straight forward, but the handling of object members requires attention. The invocation of an
object predicate will get "back" in the context of the object to which the member belongs.
Example Assume the following declarations:
interface actionEventSource
domains
actionListener = (actionEventSource Source) procedure (i).
predicates
addActionListener : (actionListener Listener) procedure (i).
... end interface
Also assume a class button_class which supports the actionEventSource. The event is sent when
the button is pressed. In myDialog_class class, which implements a dialog, I create a button and I
want to listen to its action events, so that I can react on button presses:
implement myDialog_class
clauses
new() :OkButton = button_class::n e w(...),
OkButton:addActionListener(onOk),
...
facts
okPressed : () determ.
predicates
onOk : actionListener.
clauses
onOk(Source) :assert(okPressed()).
end implement
The important thing about the example is that onOk is an object member and that, when the button
is pressed, the invocation of the registered onOk will bring us back in the object that owns onOk.
This means that we have access to the object fact okPressed, so that we can assert it.
Format Strings
A formal parameter to a predicate can be marked as format string using the attribute formatString.
The format string can contain ordinary characters which are printed without modification, and format
fields,that%beginswiththepercent'%'sign.Ifthepercentsignisfollowed%bysomeunknown
character (not the format specifier) -then%thischaracterwill be printed without modifications.
predicates
writef : (string Format [formatstring], ...).
Page 64 / 167
Format real's in fixed-decimal notation (such as 123.4 or 0.004321). This is the default
for real's.
Format real's in the shortest of f and e format, but always in e format if exponent of
the value is less than -4 or greater than or equal to the precision. Trailing zeros are
truncated.
d or
Format as a signed decimal number.
D
u or
Format as an unsigned integer.
U
x or
Format as a hexadecimal number.
X
o or
Format as an octal number.
O
c
Format as a char.
Format as a string.
Integral Domains
Integral domains are used for representing integral numbers. They are divided in two main categories
for signed and unsigned numbers. Integral domains can also have different representation size. The
predefined domains integer and unsigned represent signed and unsigned numbers with natural
representation length for the processor architecture (i.e. 32bit on a 32bit machine, etc).
IntegralDomain:
DomainName-opt IntegralDomainProperties
Language Reference
Page 65 / 167
IntegralSizeDescription:
bitsize DomainSize
DomainSize:
IntegralConstantExpression
An integral size description declares the size DomainSize of the integral domain, measured in bits.
The compiler implement such representation to the integral domain, which has no less than the
specified number of bits. The value of DomainSize should be positive and no greater than the
maximal value supported by the compiler.
If integral size description is omitted, then it will become the same as the parent domain. If there is
no parent domain, it will become the natural size for the processor.
IntegralRangeDescription:
[ MinimalBoundary-opt .. MaximalBoundary-opt ]
MinimalBoundary:
IntegralConstantExpression
MaximalBoundary:
IntegralConstantExpression
An integral range description declares the minimal MinimalBoundary and the maximal
MaximalBoundary limits for the integral domain. If a limit is omitted, then the range of the parent
domain is used. If there is no parent domain, then the DomainSize is used to determine respectively
maximum or minimum value.
Notice that the specified minimum value should not exceed the specified maximum value. That is:
MinimalBoundary <= MaximalBoundary
Also the minimal MinimalBoundary and the maximal MaximalBoundary limits should satisfy the
limits implied by the specified bit size bitsize.
The domain bit size DomainSize value and values of the minimal MinimalBoundary and the maximal
MaximalBoundary limits must be calculated while compiling time.
Real Domains
Real domains are used to represent numbers with fractional parts (i.e. floating point numbers). Real
domains can be used to represent very large and very small numbers. The built-in domain real have
the natural precision for the processor architecture (or the precision given by the compiler).
Language Reference
Page 66 / 167
If a DomainName is stated in front of the RealDomainProperties, then this domain must itself be
a real domain and the resulting domain will be a subtype of this domain. In that case
RealDomainProperties may not violate the possibility of being a subtype, i.e. the range cannot be
extended and the precision cannot be increased.
RealDomainProperties: one of
RealPrecisionDescription RealRangeDescription-opt
RealRangeDescription RealPrecisionDescription
RealPrecisionDescription:
digits IntegralConstantExpression
The real precision description declares precision of the real domain, measured in number of decimal
digits. If precision is omitted then it will become the same as for the parent domain. If there is no
parent domain, then it will be the natural precision for the processor or given by the compiler (in
Visual Prolog v.6 the compiler limit is 15 digits). Precision have an upper and a lower limits given by
the compiler, if the precisions larger than that limit is used the numbers will only obtain the processor
(compiler) specified precision anyway.
RealRangeDescription:
[ MinimalRealBoundary-opt .. MaximalRealBoundary-opt ]
MinimalRealBoundary:
RealConstantExpression
MaximalRealBoundary:
RealConstantExpression
Generic Domains
This section contains the formal syntax for generic domains, for a more complete introduction to
generics please see the section Generics.
FormalTypeParameterList:
TypeVariable-comma-sep-list-opt
Language Reference
Page 67 / 167
A TypeVariable is an upper case identifier. In a domain declaration the type variable must be bound
in the FormalTypeParameterList on the left hand side of the domain definition. In a predicate
declaration all free type variables are implicitly bound and scoped to that predicate declaration.
TypeApplication:
TypeName {TypeExpression-comma-sep-list-opt }
A TypeApplication is the application of a typeName to a list of types. The type name must be
generic and the number of formal type parameters must match the number of type expressions.
Language Reference
Page 68 / 167
Constant Definitions
A constant definition defines a named constant, its type, and its value.
ConstantDefinition: one of
ConstantName = ConstantValue
ConstantName : TypeName = ConstantValue
ConstantName:
LowerCaseIdentifier
The ConstantValue should be an expression, which can be evaluated at compile time and it should
have the type of the correspondent domain. The ConstantName should be a lower case identifier.
The TypeName can be omitted only for the following built-in domains:
1. Numerical (i.e. integral or real) constants. In this case, the corresponding anonymous numerical
domain is adopted for a constant (see the numerical domains for details).
2. Binary constants.
3. String constants.
4. Character constants.
Example
constants
my_char = 'a'.
true_const : boolean = true.
binaryFileName = "mybin".
myBinary = #bininclude(binaryFileName).
Language Reference
Page 69 / 167
The keyword class can be used only inside class implementations, since:
l
l
Predicate Declarations
The predicate declaration is used to declare the predicate in scopes in which the predicate
declaration can be seen. When predicates are declared in an interface definition, this means that
objects of the corresponding type must support these predicates. When predicates are declared in a
class declaration, this means that the class publicly provides the declared predicates. And if
predicates are declared in a class implementation, this means that the predicates are available
locally. In all cases a corresponding definitions of the predicates must exist.
PredicateDeclaration :
PredicateName : PredicateDomain LinkName-opt
PredicateName : PredicateDomainName LinkName-opt
LinkName :
a s StringLiteral
PredicateName :
LowerCaseIdentifier
Here predicateDomainName is the name of a predicate domain declared in the domains section.
A predicate declaration states the name of the predicate, its type, mode, flow (see predicate
domains), and optionally a link name.
Only class predicates can have link names. If the link name is not stated then a link name is derived
from the predicate name, the way this name is derived depends on the calling convention.
If the calling convention is apicall then the link name stated in the as clause is decorated anyway. If
this decoration is unintended, use stdcall instead.
Decorated
Sometimes a name must have the _...@N decoration, but the default from apicall is wrong. In such
cases decorated, decoratedA and decoratedW can be used to control the decoration:
Language Reference
Page 70 / 167
In this case the link name will be "_MyPredicate@4", where apicall would make it
"_MyPredicateW@4".
predicates
myPredicate : (pointer X) language stdcall a s decoratedA.
In this case the link name will be "_MyPredicateA@4", where apicall would make it
"_MyPredicate@4".
predicates
myPredicate : (pointer X) language stdcall a s decoratedW.
In this case the link name will be "_MyPredicateW@4", where apicall would make it
"_MyPredicate@4".
All of them change the start of the name from xxxx to _Xxxx and all of them put @N behind. The first
never uses a suffix; the second always uses A and the third always uses W. This means that the
programmer is responsible for deciding which suffix is needed. But he needs not to worry about
calculating argument size and initial "_X".
Constructors Sections
A constructors section declares a set of constructors. The constructors belong to the scope in
which the constructors section occurs (see class declaration and class implementation).
ConstructorsSection :
constructors ConstructorDeclaration-dot-term-list-opt
Constructor sections can only occur in declarations and implementations of classes that construct
objects.
Constructor Declarations
A constructor declaration declares a named constructor of a class.
A constructor actually has two associated predicates:
l
l
An associated constructor object predicate is used to perform an object initialization. This predicate
can only be called from the constructor in the class itself and from a constructor in a class that
inherits from the class (i.e. base class initialization).
ConstructorDeclaration :
Language Reference
Page 71 / 167
It is illegal to state a predicate mode for constructors, constructors always have procedure mode.
Example Consider the following class:
class test_class : test
constructors
new : (integer Argument).
end class test_class
Whereas the associated object level predicate has the following signature:
predicates
new : (integer).
The first call to test_class::new does not return a value, therefore it is a call to the non-function
object version of the constructor. I.e. it is an invocation of the base class constructor on "This".
The second call on the other hand does return a value, therefore it is a call to the class function
version of the constructor. I.e. we are creating a new object.
there will be no support conflict with any predicates from the origin interface;
they can be inherited as the predicates from the origin interface.
Language Reference
Page 72 / 167
PredicatesFromInterface :
predicates from InterfaceName PredicateNameWithArity-comma-sep-list-opt
interface bbb
predicates from aaa
ppp
predicates
rrr : ().
end interface bbb
Even though aaa and bbb both declare a predicate ppp, ccc can support them both without any
conflicts, because ppp has aaa as an origin interface in all cases.
Example
interface aaa
predicates
ppp : ().
qqq : ().
end interface aaa
interface bbb
predicates from aaa
ppp
predicates
rrr : ().
end interface bbb
aaa_class can inherit ppp from bbb_class, because ppp in both classes has aaa as origin
interface.
Arity
A predicate that takes N arguments are said to be N-ary, or to have arity N. Predicates with different
Language Reference
Page 73 / 167
Arity : one of
/ IntegerLiteral Ellipsis-opt
/ IntegerLiteral Ellipsis-opt ->
In Name/0... and Name/0...->. the zero is optional and can thus be written as Name/... and
Name/...->, respectively.
programPoint
A programPoint is a value that represents a specific point in a clause. The programPoint contains
the class name, the predicate name, the line number and position on the line. The programPoint
domain is defined in the core class
programPoint's are used by the exception mechanism to indicate where exceptions are raised and
continued, but the usage is not limited to that purpose.
The compiler suppors programPoint's in a special way by means of the attribute programPoint,
which can be added to a predicate declaration like this:
predicates
raiseAnException : (integer X) [programPoint].
Adding this attribute actually means that two predicates are declared, the one you have mentioned
and an another one with name raiseAnException_explicit which in addition to the arguemnts of
raiseAnException takes a programPoint as first argument:
predicates
raiseAnException : (integer X).
Language Reference
Page 74 / 167
When you call raiseAnException the compiler will actually create a program point ans call
raiseAnException_explicit instead.
Example
clauses
test() :raiseAnException(17).
where the program point corresponds to the point where raiseAnException is called in the test
predicate.
If you have a programPoint you can directly call the explicit predicate with it.
Example
clauses
raiseAnExceptio17_explicit(ProgramPoint) :raiseAnException_explicit(ProgramPoint, 17).
Typically, as in this example, explicit predicates will call other explicit predicates with the
programPoint they receive in order to use an "original" call point in a nested explicit predicate.
Such code is treated in the usual way. I.e. when calling raiseAnException or
raiseAnException_explicit will in both cases result in calling raiseAnException_explicit, so this is
the only predicate that needs an implementation. In fact, it is illegal to state clauses for the nonexplicit predicate that will never be called.
There is also a built-in predicate, programPoint/0->, which returns a programPoint corresponding
to the place where it is called.
To summarize:
l
A predicate declaration with programPoint attribute actually declares two predicates. A nonexplicit and an explicit predicate.
Calling the non-explicite predicate actually results in calling the explicit predicate with the call
point as aditina argument.
Only the explicit predicate should be implemented.
The introduction of the the programPoint feature simplifies the exception mechanism as known in
Visual Prolog 7.3 and before. For example classInfo predicates are no longer needed (though they in
Visual Prolog 7.4 are still legal but deprecated to ease transition).
Language Reference
Page 75 / 167
Language Reference
Page 76 / 167
Properties Sections
A properties section declares a set of object or class properties in the current scope.
PropertiesSection :
class-opt properties PropertyDeclaration-dot-term-list-opt
The keyword class can be used only inside class implementations, since:
l
l
Property Declaration
PropertyDeclaration :
PropertyName : PropertyType FlowPattern-list-opt
FlowPattern: one of
(i)
(o)
It is possible to get the value of a property that has the (o) flow, and it is possible to set the value
of a property that has the (i) flow. If the flow patterns are not stated, both (i) and (o) are assumed,
so it is possible bot to set and get the value of such properties.
Though it is legal to state (i) and (o) simultaneously, it is considered better practice to omit them in
the get+set case.
Example Assume we declare them with an i/o pattern as:
properties
durationO : real (o). % a get only property
durationI : real (i). % a set only property
durationIO : real (i) (o). % a "full" property, which can both be set and get.
duration : real. % equivalent to the declaration above and preferred.
Page 77 / 167
duration and scale are get+set properties, and initialized is a get-only property.
Properties are used like fact variables. It is possible to qualify properties for with a scope name or an
object.
Example X is an object that supports the interface ip
X:duration := 5,
if true = X:initialized then ... else ... end if,
X:scale := 2.56,
....
Inside an implementation of a class that supports ip you access the properties as if they were facts.
duration := 5,
if true = initialized then ... else ... end if,
scale := 2.56,
....
Implementation
A property is implemented by defining a function for getting the value and a predicate to set it.
Example
clauses
% implementation of the get function of the duration property
duration() = duration_fact.
clauses
% implementation of the set predicate of the duration property
duration(D) :duration_fact := D / scale.
Alternatively the property can be implemented as a fact variable with the same name as the
property.
Example
facts
% the initialized property is implemented by a fact variable
initialized : boolean := false.
% the scale property is implemented by a fact variable
scale : real := 1.2
In this case the compiler will implicitly provide clauses that implement the get and set predicates.
Example For the two fact variable implementations above the compiler will provide clauses
corresponding to this
Language Reference
Page 78 / 167
clauses
% implicit get clause for the scale property
scale() = scale.
clauses
% implicit get clause for the scale property
scale(V) :scale := V.
It is not possible to use the duration predicates as predicates (they are not declared as predicates,
but as a property; it is just the way the get and set of the property are implemented).
But in the predicate names are "used" - so you cannot declare predicates duration\1 or
duration\0->.
As mentioned above properties are always implemented by get/set predicates even when the program
implement them by a fact variable.
Page 79 / 167
there will be no support conflict with any properties from the origin interface;
they can be inherited as the properties from the origin interface.
PropertiesFromInterface :
properties from InterfaceName PpropertyName-comma-sep-list-opt
interface bbb
properties from aaa
pp
properties
rr : string.
end interface bbb
Even though aaa and bbb both declare a property pp, ccc can support them both without any
conflicts, because pp has aaa as an origin interface in all cases.
Example
interface aaa
properties
pp : integer.
qq : boolean.
end interface aaa
interface bbb
properties from aaa
pp
properties
rr : string.
end interface bbb
aaa_class can inherit pp from bbb_class, because pp in both classes has aaa as origin interface.
Language Reference
Page 80 / 167
Language Reference
Page 81 / 167
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 ) FfactMode-opt
FactName :
LowerCaseIdentifier
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.
Language Reference
Page 82 / 167
InitialValue :
:= ConstantValue
:= erroneous
FactVariableName :
LowerCaseIdentifier
A constant value ConstantValue should be a term (of the Domain type), which can be evaluated at
compile time.
The constant value can be omitted only if the fact variable is initialized in a constructor. Class fact
variables should always have an initial constant value.
Notice that 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.
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.
Language Reference
Page 83 / 167
Language Reference
Page 84 / 167
Clauses
Clauses are used to define predicates. A single predicate is defined by a set of clauses. Each clause
is executed in turn until one of them succeeds, or there is no more clauses left to execute. If no
clause succeeds the predicate fails.
If a clause succeeds and there are more relevant clauses in a predicate left, the program control can
later backtrack to the clauses of this predicate to search for other solutions.
Thus, a predicate can fail, succeed, and even succeed multiple times.
Each clause has a head and optionally a body.
When a predicate is called the clauses are tried in turn (from top to bottom). For each clause the
head is unified with the arguments from the call. If this unification succeeds then the body of the
clause (if such one exist) is executed. The clause succeeds if the match of the head succeeds and
the body succeeds. Otherwise it fails.
A clause consists of a head and an optional body.
Clause :
ClauseHead ReturnValue-opt ClauseBody-opt .
ClauseHead :
LowercaseIdentifier ( Term-comma-sep-list-opt )
ReturnValue :
= Term
ClauseBody :
:- Term
Page 85 / 167
The goal section consists of a clause body. The goal section defines its own scope, therefore all
invocations should contain class qualifications.
The goal must have procedure mode.
Language Reference
Page 86 / 167
Syntactically the two kinds have a huge overlap and therefore the syntax unites the two kinds into
terms.
The following definition of Term is simplified, in the sense that it includes syntactic constructions
that are not legal. For example, one cannot legally write ! + !. We do however believe that using this
simple syntax description in combination with intuitive understanding of language concepts, the type
system, and the operator hierarchy described below is better for most purposes.
Term:
( Term )
Literal
Variable
Identifier
MemberAccess
PredicateCall
PredicateExpression
UnaryOperator Term
Term Operator Term
Cut
Ellipsis
FactvariableAssignment
Backtracking
The evaluation of a Prolog program is a search for a "solution" to the goal. Each step in the search for
a solution can either succeed or fail. At certain points in the program execution there are more than
one possible choices for finding a solution. When such a choice point is met a so called backtrack
point is created. A backtrack point is a recording of the program state plus a pointer to the choice
that was not executed. If it turn out that the original choice could not provide the solution (i.e. if it
fails), then the program will backtrack to the recorded backtrack point. Thereby restoring the
program state and pursuing the other choice. The mechanism will be described and exemplified in
details in the following sections.
Literals
Literals have universal type.
Literal:
IntegerLiteral
RealLiteral
CharacterLiteral
StringLiteral
BinaryLiteral
ListLiteral
CompoundDomainLiteral
Page 87 / 167
The variable consisting of single underscore character (i.e. _) is known as the anonymous variable.
The anonymous variable is used in patterns and bindings where the corresponding value is of no
interest and should be ignored. Every occurrence of the anonymous variable is an independent
anonymous variable, i.e. even though the anonymous variable is used several times in a single clause
they have no relation to each other.
If variables that starts with an underscore are not anonymous, but they are still intended for values
of no interest that should be ignored. The compiler will issue a warning if the value of such a warning
is actually not ignored.
Prolog variables are local to the clause in which it occurs. That is, if two clauses each contain a
variable called X, these X-s are two distinct variables.
A variable is said to be free when it is not yet associated with a term and to be bound or instantiated
when it is unified with a term.
The Visual Prolog compiler does not make a distinction between upper and lower case letters in
names, except for the first letter. This means that the two variables SourceCode and SOURCECODE
are the same.
Identifier
Identifier:
MemberName
Language Reference
Page 88 / 167
MemberName:
LowerCaseIdentifier
Identifiers are used to refer to named entities (i.e. classes, interfaces, constants, domains,
predicates, facts, ...).
An identifier can just be a lower case identifier (i.e. a lowercase letter followed by a sequence of
letters, numbers and underscore characters).
Many entities can have the same name. So it may be necessary or desirable to qualify the lowercase
identifier the name of the particular scope of interest, or to state that the name is in the global
namespace.
Example These are examples of unqualified identifiers:
integer
mainExe
myPredicate
Example The built-in domain integer is defined in the global scope, to avoid ambiguity or stress that
it is this particular domains you can use the global scope member name:
::integer
NamespacePrefix:
NamespaceIdentifier-opt \
ScopeName:
LowercaseIdentifier
Language Reference
Page 89 / 167
Predicate Call
A predicate call have the form
PredicateCall:
Term ( Term-comma-sep-list-opt )
The first term must be an expression that evaluates to a value with predicate type. Typically, it is
either the name of a predicate in a class, or an expression that evaluates to a predicate member of
an object.
Notice that some predicates return values, whereas other predicates do not. A predicate that returns
a value is an expression, and the predicate call is often referred to as a function call. A predicate
that does return a value is a formula.
A predicate is invoked by applying arguments to the predicate. The predicate must have a flowpattern that matches the free/bound state of the arguments.
Most predicates are defined by a set of clauses, but some predicates are built into the language and
some are defined externally in a DLL (perhaps in a foreign programming language).
When a predicate is invoked by a predicate call, each clause is executed in turn until one of them
succeeds, or there are no more clauses left to execute. If no clause succeeds the predicate fails.
If a clause succeeds and there are more relevant clauses left, a backtrackpoint is created to the
next relevant clause.
Thus, a predicate can fail, succeed, and even succeed multiple times.
Each clause has a head and optionally a body.
When a predicate is called the clauses are tried in turn (from top to bottom). For each clause the
arguments in the head is unified with the arguments from the call. If this unification succeeds then
the body of the clause (if present) is executed. The clause succeeds, if the match of the head
succeeds and the body succeeds. Otherwise it fails.
Example
clauses
ppp() :- qqq(X), write(X), fail.
qqq(1).
qqq(2).
qqq(3).
When ppp is called it in turn calls qqq. When qqq is called, it first creates a backtrack point pointing
Language Reference
Page 90 / 167
Unification
When a predicate is called the arguments from the call is unified with the terms in the head of each
clause.
Unification is the process of binding variables in such a way that two terms become equal, making as
few bindings as possible (i.e. leaving as much as possible open for further binding).
Variables can be bound to any kind of terms, including variables or terms containing variables.
Unification is either possible or impossible, i.e. it can succeed or fail.
Variables and terms to which they are unified have types, a variable can only be bound to a term of
the same type as the variable, or a subtype. When two variables are bound to each other they must
therefore have exactly the same type.
Unification takes place (as mentioned) between a predicate call and the clause head. It also takes
place when two terms are compared for equality.
Example Consider two terms (of the same type):
T1 = f1(g(), X, 17, Y, 17)
T2 = f1(Z, Z, V, U, 43)
We will attempt to unify these two terms from left to right (i.e. a left-to-right pre-traversal).
Both T1 and T2 are f1/5 terms, this match. Therefore we attempt to unify each of the arguments
from T1 with each correspondent argument of T2. First we must unify Z and g(), this can be unified
if we bind Z to g(). So far everything is fine and we have the first binding in our unifier:
Z = g()
The next two arguments are X and Z, which already has been bound to g(). These two arguments
can also be unified if we also bind X to g(). So we now have the following contributions to our
unifier:
X = Z = g()
Next we must bind V to 17 and then we must bind Y to U. So far everything unifies with the following
unifier:
Language Reference
Page 91 / 167
But we have not yet unified the two last arguments, which are 17 and 43. No variable binding can
make these terms equal, so all in all the unification fails.
T1 and T2 cannot be unified.
In the example above T1 could have been a predicate call and T2 a clause head. But they could also
have been two terms that were compared with equal "=".
Matching
Matching is the same as unification except that variables can only be bound to grounded terms. A
grounded term is a term that does not contain any unbound variables.
It is the flow-patterns that are stated for predicates, that make it possible to use matching rather
than full-blown unification.
Example
clauses
ppp(Z, Z, 17).
qqq() :ppp(g(), X, 17).
Unification of the ppp-call with the ppp-clause is possible with the following unifier:
Z = X = g()
If ppp have the flow (i,o,i) then the unification is just a match:
l
l
It is the flow-pattern that makes it possible to predict that the clause does not need real unification.
Page 92 / 167
l
l
l
all matching/unification that does not require any evaluation is performed before any evaluation
is performed;
then evaluation corresponding to input arguments is performed one by one left-to-right.
Comparing each value to the corresponding input after each evaluation;
then the clause body is evaluated;
then the output arguments are evaluated (left-to-right);
then the return value (if the predicate is a function) is evaluated.
If any of these fail then the rest of the evaluation is not carried out.
All in all the base principles are:
l
l
l
Anonymous Predicates
An anonymous predicate is an expression that evaluates to a predicate value. The predicate value
can be bound to a variable, passed as arguments or returned as result, but the value does not have
a name in any class, interface or implementation.
Anonymous predicates have the ability to capture values from the context in which the expression
occurs, this is a rather powerful ability that can be used to avoid rather excessive amount of
strange/unpleasant code.
Syntax
Anonymous predicates are terms:
Term : one of
...
AnonymousPredicate
...
An anonymous predicate is a nameless clause in curly brackets. Certain parts are optional, giving
these forms:
AnonymousPredicate : one of
{ ( Arg-comma-sep-list ) = Term }
{ ( Arg-comma-sep-list ) = Term :- Term }
{ ( Arg-comma-sep-list ) :- Term }
{ = Term }
{ = Term :- Term }
Language Reference
Page 93 / 167
Leaving out the argument list means "the required number of arguments" and can be used whenever
the arguments are not used.
Semantics
An anonymous predicate expression evaluates to a predicate value. Consider this code:
clauses
run() :Inc = { (X) = X+1 },
A = Inc(4),
B = Inc(23),
stdio::writef("A=%,B=%", A, B).
A = 5, B = 24
class predicates
inc : (integer X) -> integer R.
clauses
inc(X) = X+1.
Where the clause (X) = X+1 can be found in the last line. I.e. this time in a named predicate.
Variables that are bound outside (i.e. before the occurrence of) an anonymous predicate can be used
inside the anonymous predicate. The value of variable will be captured by the anonymous predicate.
Variables that are bound in an anonymous predicate are local variables in the anonymous predicate.
Capturing context
An anonymous predicate can capture context, which means that it can refer to things that are
defined in its context, especially facts and variables from the clause.
Capturing Variables
Anonymous predicate occurs in a clause, and this clause may contain variables. Those variables that
are bound before the anonymous predicate is met can be used inside the anonymous predicate. This
code illustrates how a variable is captured:
Language Reference
Page 94 / 167
class predicates
createAdder : (integer A) -> pred Adder.
clauses
createAdder(A) = { (X) = X+A }.
clauses
run() :Add17 = createAdder(17),
A = Add17(4),
B = Add17(20),
stdio::writef("A=%,B=%", A, B).
We call createAdder with 17 as argument. So in the createAdder clause A is 17, and therefore the
result is { (X) = X+17 }. We say that the anonymous predicate has captured the variable A.
Since Add17 is a predicate that adds 17 to its argument, the output of the code will be:
A = 21, B = 37
W captures the ellipsis variable. qqq receives a zero-arity predicate, when this predicate is invoked
the captured ellipsis variable will be written to the standard output device.
Capturing Facts
An anonymous predicate can access facts. If it is created by a class predicate it can access class
facts. If it is created by an object predicate it can access both object and class facts. Consider this
code that captures a class fact:
class facts
count : integer := 0.
clauses
seq() = { () = count :- count := count+1 }.
clauses
run() :A = seq(),
B = seq(),
stdio::writef("A1=%,", A()),
stdio::writef("B1=%,", B()),
stdio::writef("A2=%,", A()),
stdio::writef("B2=%", B()).
Language Reference
Page 95 / 167
In object predicates we can capture object facts. So assuming that seq is an object predicate in
myClass, this code illustrates the capture of an object fact:
facts
count : integer := 0.
clauses
seq() = { () = count :- count := count+1 }.
clauses
run() :A = myClass::n e w():seq(),
B = myClass::n e w():seq(),
stdio::writef("A1=%,", A()),
stdio::writef("B1=%,", B()),
stdio::writef("A2=%,", A()),
stdio::writef("B2=%", B()).
In this case A and B comes from two different objects, which each have a count fact, so the output
will be:
A1 = 1, B1 = 1, A2 = 2, B2 = 2
Technically, the class version actually doesn't capture anything, it merely have access to the fact.
Likewise, the object version doesn't actually capture the fact, instead it captures This and through
This it obtains access to the object facts.
Capturing This
As described above it is possible to capture This and thereby gaining access to objects facts. The
same mechanism gives access to calling object predicates.
clauses
seq() = { () = count :- inc() }.
clauses
inc() :- count := count+1.
Nesting
Anonymous predicates can be nested:
clauses
run() :P = { (A) = { (B) = A+B } },
Q = P(3300),
R = P(2200),
stdio::writef("Q(11)=%,", Q(11)),
Language Reference
Page 96 / 167
To obtain Q we call P with 3300, so A is 3300 and Q therefore becomes { (B) = 3300+B } },
likewise R becomes { (B) = 2200+B } }. So, the output is:
Syntactic Sugar
If you don't need the arguments they can be skipped. So this code-fragment:
P = { (_) :- succeed },
Q = { (_, _) = 0 },
R = { (_, _, _) = _ :- fail }
Can be shortened down to this:
P = { :- succeed },
Q = { = 0 },
R = { = _ :- fail }
Notice that the arguments are completely skipped. If you write () it means zero arguments, whereas
skipping the arguments means "a suitable amount" of arguments.
Examples of practical usage
This section shows some cases where anonymous predicates are very handy. The examples assume
that the PFC scopes core, std, stdio, list and string are open.
Dummy predicates
Anonymous predicates are good for creating dummy predicate values:
ppp( { = true } ), % don't filter (boolean)
qqq( { :- succeed } ), % don't filter (determ)
rrr( { = 17 } ), % all rows must have height 17
Adaptation
In cases where you need a predicate and have one that is almost suitable, you can make the
adaptation using an anonymous predicate.
Index adaptation
Consider the predicate write3:
class predicates
write3 : (function{integer, string} Indexer).
clauses
write3(Indexer) :foreach I = std::fromTo(0,2) d o
write(Indexer(I), "\n")
end foreach.
Language Reference
Page 97 / 167
But using an anonymous predicate we can easily adapt the one-based array to the zero-based
usage:
% myArray is 0-based, write3 requires 1-based
Arr = { (N) = myArray(N+1) },
write3(Arr)
First
Second
Third
Parameter adaptation
In this code listChildren will call a ChildWriter predicate for each "C is the child of P"-pair:
class predicates
listChildren :
(predicate{string,string} ChildWriter).
clauses
listChildren(C W) :C W("Son1", "Father"),
C W("Son2", "Father").
We will however prefer to list the "P is the parent of C" using the predicate wParent:
class predicates
wParent : (string Parent, string Child).
clauses
wParent(P, C) :writef("%istheparentof%\n", P, C).
wParent takes the arguments in the opposite order, but we can easily adapt using an anonymous
predicate:
Swap = { (A,B) :- wParent(B,A) },
listChildren(Swap)
Language Reference
Page 98 / 167
We can also throw away arguments, for example when calling this predicate that only needs a Child:
class predicates
wKnowParent : (string Child).
clauses
wKnowParent(C) :writef("Weknowaparentof%\n", C).
Here addChildren will "add a count of children to P". Since each invocation corresponds to one child
we will call addChild supplying 1 as a "dummy" argument. The More is thus an adaptor that both
throws away an argument and supplies a dummy argument.
Filters
Assume this predicate:
class predicates
writeFiltered :
(string L, filterPredicate{integer} Filter).
clauses
writeFiltered(Label, Filter) :List = [1,2,3,4,5,6,7,8,9],
FilteredList = filter(List, Filter),
writef("%\t%\n", Label, FilteredList).
Filter is used to filter the list [1,2,3,4,5,6,7,8,9]; the filtered list and the Label are written to the
standard output.
First we use the allow-all filter:
Language Reference
Page 99 / 167
This filter simply succeeds for any element, so the output is the entire list:
All
[1,2,3,4,5,6,7,8,9]
It is just as easy to create a filter that fails for all elements and thus allow-none:
None = { :- fail },
writeFiltered("None", None)
None
[]
We can also create filters for elements greater than 3 and elements dividable by 3:
GreaterThan3 = { (X) :- X > 3 },
writeFiltered("> 3", GreaterThan3),
Rem3 = { (X) :- 0 = X rem 3 },
writeFiltered("Rem3", Rem3)
> 3
Rem3
[4,5,6,7,8,9]
[3,6,9]
Sorting
The list package has a sort predicate. But sometimes the default order is not what you need.
Therefore the list package also has a predicate sortBy, which sorts the elements using a programmer
defined compare operation. Let us first consider string sorting, using this predicate:
class predicates
writeStringsSorted :
(string Label, comparator{string} Comp).
clauses
writeStringsSorted(Label, C) :List = ["John Wayne", "Uma Thurman",
"Harrison Ford", "Nicolas Cage",
"Elizabeth Taylor", "Cary Grant",
"Jerry Lewis", "Robert De Niro"],
Sorted = sortBy(C, List),
write(Label, "\n"),
foreach S = list::getMember_nd(Sorted) d o
writef("%\n", S)
end foreach.
Language Reference
Normal
Cary Grant
Elizabeth Taylor
Harrison Ford
Jerry Lewis
John Wayne
Nicolas Cage
Robert De Niro
Uma Thurman
Descending
Uma Thurman
Robert De Niro
Nicolas Cage
John Wayne
Jerry Lewis
Harrison Ford
Elizabeth Taylor
Cary Grant
Let us also sort some more complex elements. Here a person has a first name and a last name, using
this domain:
domains
person = p(string First, string Last).
Language Reference
Since the compare predicate uses left-to-right lexicographic order on the p-functor, the result is the
same as before:
Normal
Cary Grant
Elizabeth Taylor
Harrison Ford
Jerry Lewis
John Wayne
Nicolas Cage
Robert De Niro
Uma Thurman
Descending
Uma Thurman
Robert De Niro
Nicolas Cage
John Wayne
Jerry Lewis
Harrison Ford
Elizabeth Taylor
Cary Grant
But with the more complex domain we can create a comparator that will sort on last name:
LN = { (p(_,L1), p(_, L2)) = compare(L1,L2) },
writePersonsSorted("LastName", LN)
LastName
Nicolas Cage
Robert De Niro
Harrison Ford
Cary Grant
Jerry Lewis
Elizabeth Taylor
Uma Thurman
John Wayne
Capturing context
As mentioned a very powerful feature of anonymous predicates is the ability to capture context. The
examples in this section show some ways you can use this.
Background threads
The routine for starting a thread takes a null-ary predicate and run it in the new thread. But you
nearly always need to pass some input data to the job in the new thread. This is possible in several
ways, but the absolutely simplest way is to use anonymous predicates. The project bgDemo from
the Visual Prolog example collection (that can be installed from the IDE) use this method. The project
has a form that can start background job and display status information from the job in a jobControl
Language Reference
properties
completion : real (i).
properties
status : string (i).
The job can report completion degree by setting the completion property (range 0 to 1). Likewise,
the status property can be used to reflect the current status of the job.
The status and completion will be shown in the form together with a job name. A job is started by
calling the form's addJob predicate:
clauses
addJob(JobName, Job) :JobCtrl = jobControl::n e w(This),
JobCtrl:name := JobName,
JobCtrl:show(),
assert(jobCtrl_fact(JobCtrl)),
arrange(),
JobLog = jobLog::n e w(JobCtrl),
Action = { :- Job(JobLog) },
_ = thread::start(Action).
In this context it is the last three lines that are interesting. thread::start takes a null-ary predicate
as argument, but a job is a predicate that takes a jobLog as argument. Therefore we create an
anonymous predicate Action, which takes no arguments but invokes Job on the JobLog. The
anonymous predicate has captured both Job and JobLog from the context, and subsequently both
these values are transferred to the new thread even though this thread only receives a null-ary
predicate. The jobs in the bgDemo project are merely dummy jobs that only manipulate their
jobLog. One of them looks like this:
clauses
job(Log, From, To) :Log:status := "Step 1",
foreach N1 = std::fromTo(From, To) d o
Log:completion :=
(N1-From) / (To-From) / 2,
programControl::sleep(3)
end foreach,
Log:status := "Step 2",
foreach N2 = std::fromTo(From, To) d o
Log:completion :=
(N2-From) / (To-From) / 2 + 0.5,
programControl::sleep(3)
end foreach,
Log:status := "finished".
Language Reference
It has two loops which run from From to To and calculates the completion and sets it on the Log. It
also sets the status text before, between and after the loops. You may notice that the job does not
have the proper job type, because a proper job only has one argument (the jobLog), this job has
three arguments. Again it is anonymous predicates that help us. The code that adds the jobs to the
form looks like this:
predicates
onFileNew : window::menuItemListener.
clauses
onFileNew(_Source, _MenuTag) :JF = jobForm::display(This),
Job11 = {(L) :- job1::job(L, 1, 1000)},
Job12 = {(L) :- job1::job(L, 200, 600)},
Job13 = {(L) :- job1::job(L, 1200, 3000)},
Job14 = {(L) :- job1::job(L, 1, 1000)},
JF:addJob("job1.1", Job11),
JF:addJob("job1.2", Job12),
JF:addJob("job1.3", Job13),
JF:addJob("job1.4", Job14),
...
In a more realistic program, it is most likely that From and To would not be constants, but rather
parameters passed from some outer place. In that case these anonymous predicates would also
capture variables from the context. The jobLog in the bgDemo illustrates one more usage of
anonymous predicates. The jobLog pass the completion and the status information to a jobControl.
The jobControl is a GUI control on the jobForm capable of doing a suitable rendering of the
information. This however gives a synchronization problem, because GUI controls are not thread safe
and here we want to update some controls from a background thread. This can lead to conflicts,
because it is the main thread that draws the controls. The solution is to make transfer the the
update of the control to the GUI thread. We do this by posting actions to the control. The
implementation of the status update looks like this:
clauses
status(Status) :Action = { :- jobCtrl:status := Status },
jobCtrl:postAction(Action).
Action is a null-ary predicate that will set the status in the jobCtrl. We post this action to the
jobCtrl. When the jobCtrl receives the action it invokes it and is thus updated. This way that actual
update of the control will be performed by the GUI thread. This anonymous predicate not only
captures the Status variable it also captures the jobCtrl fact.
Asynchronous callbacks
Assume that we send commands to a remote service. The command execution is asynchronous, so
when we execute a command we also give a callback action which will be invoked when the
execution of the command is finished. To execute a command we must call this predicate:
predicates
executeCommand :
(command Cmd, predicate{} OnDone).
Based on this predicate we want to create a similar predicate that can execute a list of commands. A
certain command should be executed when the previous command completes. We will also make our
Language Reference
If the script is empty we simply invoke the OnDone action. If the script has a command H and a rest
script T, we must first execute H, and when it is finished we must execute the rest of the script T.
So the OnDone action we supply when executing H must execute T. All in all, the implementation
can look like this:
clauses
executeScript([], OnDone) :OnDone().
executeScript([H|T], OnDone) :DoneH = { :- executeScript(T, OnDone) },
executeCommand(H, DoneH).
We have used an anonymous predicate to perform the execution of the rest of the script. This
anonymous predicate captures T and OnDone.
Facts
A fact database contains a number of fully instantiated (grounded) predicate heads corresponding to
the facts from the facts section declaration. The facts can be accessed by a predicate call, using
the fact name as the predicate name. The predicate call is matched against each fact in turn;
succeeding with a possible backtrack point to the next fact each time the predicate call match the
fact. When there are no more facts in the fact database then the predicate call fails.
New facts can be asserted using the predicates assert/1, asserta/1, and assertz/1. assert/1 is the
same as assertz/1 and it asserts a new fact to the end of the list of facts, whereas asserta/1
asserts a new fact to the start of the list.
Existing facts can be retracted with the predicate retract/1 and retractAll/1. retract/1 retracts the
first fact that match the argument binding variables in the argument and leaving a backtrack point so
that more facts will potentially be retracted when backtracking.
retractAll/1 retracts all facts that matches the arguments and succeeds without any binding.
Operators
Operators are organized in a precedence hierarchy. In the rule below operators in each group have
same precedence, which is higher than those below. I.e. the power operator has higher precedence
Language Reference
UnaryOperator: one of
-+
All operators except the UnaryOperator's are binary. The power operator is right associative, all
other operators are left associative.
RelationOperator, MustUnifyOperator and InOperator have same precedence.
Notice that the placement UnaryOperator is not consistent with mathematics, where these
operators are at the same level as the AdditionalOperator's. The difference has no influence of the
calculated value, but it allows writing 2*-2, where mathematics would require a parenthesis around
the second operator 2*(-2). It also means that -2*2 is mmeans (-2)*2 where it would be -(2*2) in
mathematics (the resulting value is the same).
Example
-2^2 is the same as -(2^2) because ^ has higher precedence than unary minus.
Example
3^2^2 is the same as 3^(2^2) because ^ is right associative.
Example
-2*-3^-4+5 is the same as ((-2) * (-(3 ^ (-4)))) + 5.
Example The following term:
7 + 3 * 5 * 13 + 4 + 3 = X / 6A < 7, p(X)
I.e. at outermost level the term is an "or" of two terms, the first of these is a relational (=) term, the
second is an "and" term, etc.
Language Reference
MultiplicationOperator: one of
* / div mod quot rem
AdditionOperator: one of
+ -
Relational Operators
The relational operators are formulas, which takes expressions as arguments. Given this nature they
are non-associative.
RelationOperator: one of
= > < >= <= <> ><
First the left term is evaluated, then the right term is evaluated and then the results are compared.
Notice that <> (different) is not the dual operation of = (equal). <> compares two values, whereas
= tries to unify two terms (in the general case at least).
The dual to expression A = B is not (A = B).
Must Unify Operator
The must unify operator is a procedure, which takes expressions as arguments. It is non-associative.
MustUnifyOperator:
==
A == B unifies A and B; if the unification fails an exception is raised, otherwise the predicate
succeeds. Therefore A == B always succeeds.
Example
clauses
p(L) :[H|T] == L,
...
p is a procedure. An exception will be raised if it is called with an empty list, because [H|T] and L
Language Reference
The in operator is used to test for member ship of a collection (e.g. a list) and to nondeterministically
generate the members of a collection.
Example
predicates
p : (Type X, Type* L).
clauses
p(X, L) :if X in L then
write("X is in L\n")
end if.
p is a procedure that takes a value X and a list L as arguments. If X is in the list L then it will write
"X is in L". In this case in is used as an in-test (membership test).
Example
predicates
q : (Type* L).
clauses
q(L) :foreach X in L d o
writef("% is in L\n", X)
end foreach.
q is a procedure that takes a list L as argument. The "in" operator is used to nondeterministically
return the members of L, so that they can be written.
The in operator can be defined for any domain and interface using the in_test and in_iterate
attributes.
The in_test(<predicate name>) attribute defines the predicate that is used as in-test for a
certain domain or interface. Likewise the in_iterate attribute defines the predicate that is used as
in-ieratorfor the domain/interface.
Example
domains
tree{E} = empty; node(tree{E} Left, E Value, tree{E} Right) [in_test(isMemberTree), in_iterate(getAll_nd
When the program contains A in B where A is bound B is a tree{E} then isMemberTree is actually
Language Reference
Example
interface collection [in_test(contains), in_test(getAll_nd)]
...
end interface collection
When the program contains A in B where A is bound B is a collection then contains is actually
called.
In that case A in B corresponds to B:contains(A).
If A is free the call corresponds to A = B:getAll_nd().
For a domain <collection> the in_test and in_iterate predicate must fulfill these schematic
declarations:
domains
<collection> = ... [in_test(<in_test>), in_iterate(<in_iterate>)].
class predicates
<in_test> : (<some-type> Elem, <collection> Collection) determ.
<in_iterate : (<collection> Collection) -> <some-type> Elem nondeterm.
For an interface <collection> the in_test and in_iterate predicate must fulfill these schematic
declarations:
interface <collection> [in_test(<in_test>), in_iterate(<in_iterate>)].
predicates
<in_test> : (<some-type> Elem) determ.
<in_iterate : () -> <some-type> Elem nondeterm.
...
end interface <collection>
The in operator is predefined on list domains, and in PFC the collections have suitable attributes.
Example
clauses
p() :foreach X in [1, 2, 3, 4] d o % in_iterate
if X in [2, 4] then % in_test
...
Language Reference
The first in is the predefined in_iterate for the list domain, and the second one is the predefined
in_iterate.
clauses
q() :M1 = setM_redBlack::n e w(),
M1:inset("a"),
M1:inset("b"),
M2 = setM_redBlack::n e w(),
M2:inset("b"),
foreach X in M1 d o % in_iterate
if X in M2 then % in_test
...
end if
end foreach.
predicates
contains : (@Type Value) determ.
% @short Succeeds if the collection contains the value @Type
% @end
predicates
getAll_nd : () -> @Type Value nondeterm.
% @short @Type is nondeterministic iteration of the elements in the collection.
% @end
...
Logical Operators
The AndOperator(s) and OrOperator(s) are formulas, which takes formulas as arguments. They are
all left associative. The , and and are synonyms and so are ; and or.
AndOperator: one of
, and
OrOperator: one of
or orelse
and (,)
The evaluation of an and term A, B proceeds as follows. First the left sub-term A is evaluated. If this
evaluation fails, the whole and term fails. If A succeeds then the right sub-term B is evaluated. If
this evaluation fails, the whole and term fails, otherwise the and term succeeds.
Language Reference
When ppp is called it will first call qqq and if qqq succeeds, then it will call rrr. If rrr succeeds, then
the and term and subsequently the whole clause succeeds.
or (;)
The evaluation of an or term A; B proceeds as follows. First a backtrack point to the second term B
is created and then the first term A is evaluated. If the evaluation of the first term succeeds, then
the whole or term succeeds and is left with a backtrack to the second term B. If the evaluation of
the first term fails, the backtrack point to the second term is activated.
If the backtrack point to the second term B is activated (either because the first term fails, or
because something later in the execution invokes the backtrack point), then the second term B is
evaluated and the whole or term will succeed if B succeeds.
Thus an or term can succeed with a backtrack point and the second sub-term B is only evaluated on
backtrack.
Example
clauses
ppp() :(V = 3 or V = 7), write(V), fail.
Here we have used the keyword or, but you can also use semi-colon ;.
When ppp is called we first create a backtrack point to the term V = 7 and then we evaluate V = 3.
Thereby V will be bound to 3 we then continue to the write(V) after 3 has been written fail is met.
fail always fails so we effectuate the backtrack point leading us to the term V = 7.
Backtracking also undo all bindings made since the backtrack point was created. In this case it means
that V is unbound.
Then V = 7 is evaluated and V becomes bound to 7 and er continue to the term write(V), and then
fail is met again, this time there are no more backtrack points ppp fails.
Using parentheses or can be nested deeply in clauses.
clauses
p(X) = Y :(X = 1, !, Z = 3 or Z = 7), Y = 2*Z.
Language Reference
orelse
orelse is a deterministic pendant to the nondeterministic or. A orelse B will succed if A succeds or if
B succeds, but it will not leave a backtrack point to B if A succeeds.
The evaluation of an orelse term A orelse B proceeds as follows: First a backtrack point to the
second term B is created and then the first term A is evaluated. If the evaluation of the first term
succeeds then the backtrack to the second term (and any backtrack point within it) B are removed
again and the whole orelse term succeeds. If the evaluation of the first term A fails, the backtrack
point to the second term B is evaluated.
So an orelse term does not leave a backtrack point.
Example
clauses
ppp(V) :(V = 3 orelse V = 7), write(V).
Whenever ppp is called we first create a backtrack point to the term V = 7 and then we evaluate
the term V = 3, if V = 3 succeds we remove the backtrack point to V = 7 again and then continue to
write(V). If V = 3 fails the backtrack point to V = 7 is effectuated. If V = 7 succeds we continue to
write(V), if V = 7 fails the entire ppp predicate will fail.
not
The not/1 takes a term as the argument. The evaluation of not(A) first evaluates A. If A succeeds,
then not(A) fails, if A fails, then not(A) succeeds.
Notice that not(A) will never bind any variables, because if not(A) succeeds then A has failed, and
a failed term does not bind anything. If not(A) on the other hand fails, it cannot bind any variables
either, because then the term itself failed.
Also notice that not(A) can never succeed with backtrack points, because if not(A) succeeds then
A have failed, and a failed term cannot contain any backtrack points. This in turn means that all
possibilities of success in A have been exhausted.
Language Reference
Example
clauses
ppp(X) :X > 7,
!,
write("Greater than seven").
ppp(_X) :write("Not greater than seven").
When ppp is executed, there is first created a backtrack point to the second clause, and then the
first clause is executed. If the test "X > 7" succeeds then the cut "!" is reached. This cut "!" will
remove the backtrack point to the second clause.
Example
clauses
ppp() :qqq(X),
X > 7,
!,
write("Found one").
ppp() :write("Did not find one").
clauses
qqq(3).
qqq(12).
qqq(13).
When ppp is executed it first creates a backtrack point to the second ppp clause and then qqq is
called. The qqq will create a backtrack point to the second qqq clause and execute the first clause,
thereby returning the value 3. In ppp variable X is bound to this value and then compared to 7. This
test fails and, therefore, the control backtracks to the second clause of qqq.
Before executing the second clause a new backtrack point to the third clause of qqq is created and
then the second clause returns 12.
This time the test against 7 succeeds and, therefore, the cut is executed. This cut will remove both
the backtrack point left in qqq as well as the backtrack point to the second clause of ppp.
Cut Scopes
A cut scope is a scope to which the effect of a cut is limited. Meaning that if a cut is met within a
cut scope then only backtrack points within that scope are discarded, while backtrack points outside
(i.e. prior to) the cut scope remains.
Language Reference
aaa calls p1_nd, which leaves a backtrack point, and then it calls qqq. qqq calls p2_nd, which also
leaves a backtrack point. Then we meet a cut. This cut is in the cut-scope of the qqq predicate, so
it is only the backtrack point in p2_nd which is discarded, the one in p1_nd remains.
Several terms introduce cut scopes (see the respective terms: list comprehension, if-then-else,
foreach). Here we will use if-then-else to illustrate the effect of cut scopes. Consider the schematic
if-then-else term:
if Cond then T1 else T2 end if
The condition Cond is a cut-scope, meaning that a cut inside Cond will only have effect inside
Cond. Cuts inside T1 and T2, on the other hand, have effect outside the if-then-else statement.
Consider this code fragment:
X = getMember_nd([3,1,2]),
if X = getMember_nd([3,3]), ! then
write(X)
else
!
end if,
fail
getMember_nd is a nondeterministic predicate. The evaluation of this code will go as follows. First X
is bound to 3 and getMember_nd leaves a backtrack point (so that X can later become 1 and then
even 2).
Then we evaluate the condition in the if-then-else term. The first part of this condition succeeds as
3 is a member of [3,3]. The first part also leaves a backtrack point, so that it can be examined
whether X is a member several times.
Now we meet a cut. This cut is inside the condition part of an if-then-else statement, so it only has
local effect, meaning that it only discards the backtrack point in the second getMember_nd, but
leaves the backtrack point in the first getMember_nd predicate.
The whole condition succeeds and we enter the then-part and write out "3".
After the if-then-else we meet fail, which backtracks us to the first getMember_nd.
getMember_nd then binds X to 1, and leaves a backtrack point (so that X can later become 2).
Then we evaluate the condition in the if-then-else term. The first part of this condition fails as 1 is
Language Reference
List Comprehension
ListComprehensionTerm :
[ Term || Term ]
The list comprehension term is a list expression. Consider this schematic term:
[ Exp || Gen ]
Gen is (typically) a nondeterm term. Exp is evaluated for each solution of Gen, and the resulting
Exp's are collected in a list. The Exp corresponding to the first solution of Gen is the first element in
the list, etc. This list is the result of the list comprehension term. Exp must be procedure (or
erroneous). Both Exp and Gen are cut scopes.
The list comprehension (normally) reads: The list of Exp's such that Gen.
Example
[ X || X = getMember_nd(L), X mod 2 = 0 ]
This reads the list of X's such that X is in L and X is even. So this expression is the even numbers of
L.
Example
[ X + 1 || X = getMember_nd(L), X mod 2 = 0 ]
Here the collected expression is more complex. This makes say the term more awkward:
"the list of (X+1)'s such that ..."
This expression again finds the even elements in L, but the resulting list contains all these values
incremented.
This term is completely equivalent to this term:
[ Y || X = getMember_nd(L), X mod 2 = 0 , Y = X+1 ]
Language Reference
foreach
ForeachTerm :
foreach Term d o Term end foreach
Gen is (typically) a nondeterm term. Body is evaluated for each solution of Gen. If/when Gen fails
the foreach-term succeeds without evaluating Body. Body must be procedure (or erroneous).
Gen and Body are both cut scopes.
The schematic foreach term resembles a fail loop:
Gen,
Body,
fail
The main (and important) difference is that a foreach-term succeeds after the iteration, whereas a
fail loop fails. As a result foreach-terms can be followed by other terms and they can be properly
nested.
Example
Language Reference
LL is supposed to be a list of lists. The outer foreach-loop iterates L through this list, so each L is a
list. The inner foreach-loop iterates X through the elements of L.
There are a number of things to notice:
l
l
Example
clauses
p(L) :foreach X = list::getMember_nd(L) d o
stdio::write(X, "\n")
end foreach,
stdio::write("Finished\n").
In this context it is advantageous that Body must be a procedure, because in a "fail-loop" the body
may accidentally fail before reaching the fail in the loop. Another advantage is that a foreach loop
succeeds when the loop is finished, whereas a fail loop fails, so that execution can continue in the
same clause. Also notice that foreach can be properly nested:
clauses
p(LL) :foreach L = list::getMember_nd(LL) d o
foreach X = list::getMember_nd(L) d o
stdio::write(X, "\n")
end foreach,
stdio::write("Finished a list\n")
end foreach,
stdio::write("Finished all lists\n").
if-then-else
Language Reference
Elseif:
elseif Condition then Term
Else:
else Term
You can use "and" and "or" logical operators and other "complex" terms in all three sub-terms.
Language Reference
There is no comma before the keywords "then", "elseif", "else", and "end if".
For readability sake, we always recommend using "or" instead of ";". Likewise we also recommend
using "and" (instead of ",") when it (as in the condition above) represents a logical "condition" rather
than a "sequentation".
Leaving out the else-part is just shorthand for writing that else succeed, i.e.
if Cond then Term end if
is short-hand for
if Cond then Term else succeed end if
if-then-else (expression)
The if-then-else expression conditionaly evaluates excressions.
Syntactically it is same as the if-then-else statement, but and the terms in the branches must be
expressions and the entire if-then-else expression will itself evaluate to a value.
The shorhand writings that leave out the else-part does not make sense for the expression.
Example
clauses
w(X, Y) :Min = if X < Y then X else Y end if,
writef("Theminimumis:%\n", Min).
The if-then-else expression above evaluates to X if X is less than Y else it evaluates to Y. Min is
bound to the resulting value.
try-catch-finally
The try-catch-finally statement provides means for dealing with exceptions that may occur in a given
block of code.
TryCatchTerm:
try Term CatchFinally-list end try
CatchFinally: one of
catch Variable d o Trap-Handler
finally Finally-Handler
Handler:
Term
A try-construction thus have a Term and a list of catch and finally handlers.
Language Reference
try-catch
Consider the try-catch construction
try
Body
catch Var d o
Handler
end try
Language Reference
The purpose of the construction is to evaluate the Handler after the Body no matter how the Body
terminates, i.e. whether it succeeds, fails or terminates with an exception (it cannot leave backtrack
points).
The evaluation is like this
First Body is evaluated.
l
l
l
The Stmt is free'd also if fetch fails, or if something terminates with an exception.
Language Reference
Language Reference
In the line marked (1) we create a bb_class object: the object has construction type bb. The
variable BB is a reference to this new object. BB provides the view type bb on the object. In the
line marked (2) the object is passed to ppp as an aa object. The conversion from view type bb to
view type aa is performed implicitly. When the object reaches the line marked (3) it has view-type
aa, though the construction type is still bb.
Explicit Conversion
Language Reference
Both convert/2-> and tryConvert/2-> take a "type" as the first argument and a value of any type as
the second argument and will then return the converted value as the result.
convert/2-> will raise an exception if the conversion is impossible, while tryConvert/2-> simply fails in
that case.
Notice that the use of convert/2-> and tryConvert/2-> is always superfluous, if the source type is a
subtype of the target type, because then the conversion will be performed implicitly.
convert/2-> and tryConvert/2-> can be used in the following situations:
l
l
The compiler may complain (but does not have to) if it can determine that a conversion can never
succeed, for example if attempting to convert between number domains that does not have
overlapping ranges.
Conversion Downwards
When an object is converted to a super type (i.e. to a supported interface), then information about
the object is "forgotten". Notice that the capabilities are not really lost they are just not visible in the
context where the object is seen with a less capable interface.
In many situations it is necessary to restore the actual capabilities of the objects. Therefore, we
need to be able to convert them downward as well as upwards.
Downward conversion cannot (in general) be validated statically. Therefore, it is necessary to use
explicit conversion when restoring "lost" interfaces.
Example
While it is extremely simple to make sensible illustration of type conversions up in the supportshierarchy, it requires a "real" example to illustrate sensible use of downward conversion. Therefore we
shall present a more "real" example here.
Assume that we want to implement "sets of homogeneously typed objects". I.e. sets of objects which
Language Reference
Now assume that we have some object type myObject and that we want to create the
corresponding "set" class myObjectSet_class. We declare myObjectSet_class as following:
interface myObjectSet
predicates
insert : (myObject Elem).
getSomeElem : () -> myObject determ.
...
end interface
class myObjectSet_class : myObjectSet
end class
I.e. myObjectSet has all the predicates of objectSet but every occurrence of object is replaced
with myObject. The implementation of myObjectSet_class inherits from objectSet_class, this
embedded/inherited objectSet will carry the members of the set. The implementation will fulfill the
following invariant: The embedded objectSet will only contain objects of type myObject (even
though they "technically" have type object).
The implementation looks as follows:
implement myObjectSet_class
inherit objectSet_class
clauses
insert(Elem) :objectSet_class::insert(Elem). % (1)
getSomeElem() = Some :SomeObject = objectSet_class::getSomeElem(), % (2)
Some = convert(myObject, SomeObject). % (3)
...
end implement
In the line marked (1) Elem is automatically converted from type myObject to object. In the line
marked (2) we retrieve an object from the embedded object set. Technically this object has type
object. But from our invariant we know that the object also supports myObject. Subsequently, we
know that we can safely restore the myObject interface. This is explicitly done in the line marked
(3).
Language Reference
Example
predicates
interpretBufferAsString : (pointer BufferPointer) -> string Value.
clauses
interpretBufferAsString(BufferPointer) = uncheckedConvert(string, BufferPointer).
Language Reference
When errorExit/1 is called the currently active exception handler is invoked. This exception handler is
executed in its original context, i.e. in the context where it was set rather than in the context where
the exception is raised.
The argument that errorExit/1 is invoked on is transferred to the exception handler. This argument
must somehow provide the needed description of the exception.
Together with additional runtime routines, it is possible to build high-level exception mechanisms on
top of this system.
It is however out of the scope of this document to describe runtime system access routines.
It is likewise out of the scope of this document to describe how the runtime system deals with
exceptions occurring inside the runtime system.
The first argument of try-catch is the term to execute with new exception handler. The second
argument must be a variable. This variable will be bound to the value errorExit/1 is invoked on, if it is
invoked while this exception handler is active. The third argument is the exception handler, which will
be invoked if errorExit/1 is called while this exception handler is active.
The exception handler can access the variable stated in the second argument thereby examining the
exception that was raised.
Example
clauses
p(X) :try
dangerous(X)
catch Exception d o
handleDangerous(Exception)
end try.
If an exception is raised while executing dangerous, then Exception will be bound to the exception
value, and control will be transferred to the third argument of try-catch. In this case Exception is
passed to handleDangerous.
Language Reference
Operators
See also Operators.
Operator
Description
Remark
Not defined for 64 bit
integral numbers
Power operation
- (unary)
Unary minus
*, /
div, mod
quot,
rem
+, -
l
l
l
l
All binary operators takes two arguments of same base type and returns a value of that base type.
Operands and result may be converted using ordinary subtype rules.
Integral division
div and quot are different integral division operators.
l
l
div rounds towards minus infinite. mod is the remainder corresponding to div.
quot rounds towards zero. rem is the remainder corresponding to quot.
-15 7
-3
-2
-1
Language Reference
-3
-6
-2
-15 -7
-1
-1
Constants
compilation_date
Compilation date.
compilation_time
Compilation time.
A compiler version.
maxFloatDigits
null
nullHandle
invalidHandle
platform_bits
platform_name
compilation_date
Compilation date. Here Y Y Y Y means the number of a year, MM means a month number, and DD means
a day number.
compilation_date : string = "YYYY-MM-DD".
compilation_time
Compilation time. Here HH means hours, MM means minutes, and SS means seconds.
compilation_time : string = "HH-MM-SS".
compiler_buildDate
Build date of the compiler.
compiler_buildDate : string = "YYYY-MM-DD HH-MM-SS".
compiler_version
A compiler version. This value depends upon the compiler version.
compiler_version = 6003.
maxFloatDigits
Defines the maximal value of "digits", which is supported by the compiler.
Language Reference
null
A special constant of a pointer type with the zero value.
null : pointer = uncheckedConvert(pointer, 0).
nullHandle
A special constant of a handle type with the zero value.
nullHandle : handle = uncheckedConvert(handle, 0).
invalidHandle
A special constant of a handle type with the invalid (-1) value.
invalidHandle : handle = uncheckedConvert(handle, -1).
platform_bits
Defines the digital capacity of compilation platform.
platform_bits = 32.
or
platform_bits = 64.
platform_name
Defines the target platform name.
platform_name : string = "Windows 32bits".
or
platform_name : string = "Windows 64bits".
Domains
any
char
string
string8
symbol
binary
Sequence of bytes.
Language Reference
integer64
integerNative
unsigned
unsigned64
unsignedNative
real
Float-pointing number.
real32
Float-pointing number.
pointer
handle
boolean
Boolean values.
factDB
compareResult
any
Universal term type.
any
The values of this domain are any terms. Such a value contains the reference to the term type library
and a term itself.
char
Wide character.
char
The values of this domain are UNICODE characters. Implemented as 2 unsigned bytes.
Only assignment and comparison (in the lexicographical sense) operations are applied to the values of
this domain. The image of a character has the following syntax:
Char_image :
' Char_value '
Char_value :
Letter
Digit
Graphical_symbol
\ Escape_seq
Escape_seq:
t
n
r
\
'
"
Language Reference
In the syntax above HHHH correspond to 4 hexadecimal digits. Also, the backslash symbol and the
single quote can be represented by an escape-sequence only.
compareResult
The compareResult is a built-in domain. It is used to define a comparison result. The built-in
compare/2-> predicate's result is compareResult domain.
domains
compareResult = less; equal; greater.
string
Wide zero terminated sequence of wide characters.
string
A string is a sequence of UNICODE characters. It's implemented as a pointer to the wide zeroterminated array of wide characters. Only assignment and comparison (in the lexicographical sense)
operations are applied to values of this domain.
In source code a string literal can be specified as a set of sequences of characters surrounded by the
double quotes.
StringLiteral:
StringLiteralPart-list
StringLiteralPart :
@" AnyCharacter-list-opt "
" CharacterValue-list-opt "
\\ representing \
\t representing Tab-character
\n representing newline-character
\r representing carriage return
\' representing single quote
\" representing double quote
\u followed by exactly four HexadecimalDigit's representing the Unicode character
corresponding to the digits.
The double quotes in the string can be represented by the escape-sequence only (the single quote
can be represented both with an escape-sequence and a graphical symbol).
string8
Language Reference
Similar to a string, a symbol is also a sequence of the UNICODE characters. It's implemented as a
pointer to an entry in a symbol table that contains strings. The operations that can be applied to
symbols are the same as for strings.
The image of a symbol is represented with a <string_literal> (any string surrounded by the double
quotes).
Symbols and strings are largely interchangeable but they are stored differently. Symbols are kept in a
look-up table and their addresses, rather than the symbols themselves, are stored to represent
objects. This means that symbols can be matched very quickly and, if a symbol occurs repeatedly in
a program, it can be stored very compactly. Strings are not kept in a look-up table. Visual Prolog
examines strings character-by-character whenever they are to be matched.
binary
Sequence of N bytes.
binary
Values of this domain are used for holding binary data. A binary value is implemented as a pointer to
the sequence of bytes that represents the contents of a binary term.
The length of a binary term is situated in the 4 bytes immediately preceding this sequence of bytes.
The 4 bytes contains:
TotalNumberOfBytesOccupiedByBinary = ByteLen + 4
where ByteLen - is the length of the binary term and 4 is number of bytes occupied by size field.
Only assignment and comparison operations are applied to values of binary domain.
Two binary terms are compared in the following way:
l
l
Language Reference
Each expression should be calculate on compiling time and its value should be in the range from 0 to
255.
binaryNonAtomic
Sequence of N bytes.
binaryNonAtomic
Values of this domain occupy 4 bytes. Arithmetic operations (+, -, /, *, ^), comparison, assignment,
div/2->, mod/2->, quot/2->, and rem/2-> operations are applied to values of this domain.
The permitted number range is from -2147483648 to 2147483647.
The syntax for the integer literal is determined by the Integer rule:
Integer :
Add_operation-opt
Add_operation-opt
Add_operation-opt
Add_operation :
+
Oct_number :
Oct_digit-list
Oct_digit : one of
01234567
Dec_number :
Dec_digit-list
Dec_digit : one of
Oct_digit 8 9
Hex_number :
Hex_digit-list
Hex_digit : one of
Dec_digit a b c d e
0o Oct_number
Dec_number
0x Hex_number
fABCDEF
integer64
Integral signed number.
Language Reference
integer64
unsigned
Integral unsigned number.
unsigned
Values of this domain occupy 4 bytes. Arithmetic operations (+, -, /, *, ^), comparison, assignment,
div/2->, mod/2->, rem/2->, and quot/2-> operations are applied to values of this domain.
The permitted number range is from 0 to 4294967295.
The syntax for unsigned number images is the same as for integer numbers. The usage of minus
sign (UnaryMinus) is not allowed for an image of an unsigned number.
unsigned64
Integral unsigned number.
unsigned64
real
Float-pointing number.
real
Values of this domain occupy 8 bytes. This numerical real domain is introduced for the user's
convenience only. All arithmetic, comparison, and assignment operations are applied to values of real
domain.
The permitted number range is -1.7e+308 to 1.7e+308. Values from integral domains are
automatically converted to real numbers when necessary.
The syntax for the floating-point number literal is determined by the Real rule:
Real :
Add_operation-opt Fraction Exponent-opt
Fraction :
Dec_number Fractional_part-opt
Fractional_part :
. Dec_number
Exponent :
Exp Add_operation-opt Dec_number
Exp :
e
E
Add_operation :
+
Dec_number :
Dec_digit-list
Dec_digit : one of
0123456789
real32
Float-pointing number.
real32
Values of this domain occupy 4 bytes. This numerical real32 domain is introduced for the user's
convenience only. All arithmetic, comparison, and assignment operations can be applied to values of
real32 domain.
The permitted number range is -3.4e+38 to 3.4e+38.
The syntax of real32 literals is the same as real lietrals.
pointer
Language Reference
A pointer directly corresponds to memory addresses. Only the equality operation can be applied to
the values of this domain. There is a built-in null constant for this type
handle
A handle is used for Windows API function call. Values of this domain has the same size as a pointer
(i.e. 4 on 32bit platfor and 8 on 64bit platform).
There are no operations for this domain and cannot be converted (except uncheckedConvert) to/from
other domains.
There is a built-in nullHandle and invalidHandle constant for this type
boolean
Boolean values.
boolean
This domain is introduced for the user convenience only. It is treated as usual compound domain with
the following definition:
domains
boolean = false(); true().
factDB
Descriptors of named internal databases.
factDB
All user-defined names of facts sections are the constants of this domain. The compiler automatically
builds the corresponding compound terms from such constants whenever it's in need. At the runtime
the 1st field of this structure contains the address of the corresponding domain descriptor and the
2nd field contains either zero (for class facts sections) or pointer to an object (i.e. This, for object
facts sections).
Predicates
Language Reference
Term "and"
assert/1
Insert the specified fact at the end of the matched internal facts
database.
asserta/1
assertz/1
bound/1 determ
class_name/0->
This compile time predicate returns the string ClassName that represents
the name of the current interface or class.
compare/2->
convert/2->
digitsOf/1->
errorExit/1 erroneous
fail/0 failure
Invoke backtracking.
free/1 determ
fromEllipsis/1->
Creates the list of terms of the universal type any from the EllipsisBlock.
hasDomain/2
hasDomain/2->
in/2 determ
in/2 nondeterm
isErroneous/1 determ
lowerBound/1->
maxDigits/1->
not/1 determ
or/2
;/2
orelse
predicate_fullname/1->
predicate_name/1->
programPoint/0->
retract/1 nondeterm
retractall/1
Remove all matching facts from the matched internal facts database.
retractFactDb/1
Remove all facts from the specified named internal facts database.
sizeBitsOf/1->
Language Reference
sizeOfDomain/1->
sourcefile_lineno/0->
Returns the current line number in the source file processed by the
compiler .
sourcefile_name/0->
sourcefile_timestamp/0- Returns the string representing the date and time of the source file
>
processed by the compiler.
succeed/0
toAny/1->
Converts the specified Term to the value of the universal term type
any.
toBinary/1->
toBoolean/1->
toEllipsis/1->
toString/1->
toTerm/1->
toTerm/2->
upperBound/1->
findall/3
trap/3 determ Use try ... catch V do ... end try instead
and
See and (,).
assert
assert : (<fact-term> FactTerm).
Insert the specified fact at the end of the matched internal facts database
assert(Fact) inserts Fact in the matched internal facts database after any other stored facts for
Language Reference
The problem is that the retract in first line will eventually retract the fact asserted in the last line,
because that fact is inserted last in the fact chain.
Exceptions:
l
asserta
asserta : (<fact-term> FactTerm).
Attempt to a fact declared as determ, but the fact instance already exists.
assertz
assertz : (<fact-term> FactTerm).
Language Reference
This compile time predicate returns the string ClassName that represents the name of the current
interface or class.
compare
compare : (A Left, A Right) -> compareResult CompareResult.
Comparison of two terms of the same domain, resturns the value of compareResult domain.
CompareResult = compare("bar", "foo")
convert
convert : (<type> Type, Term) -> <type> Converted.
InputTerm: Specifies the value that must be converted. InputTerm may be any Prolog term
or an expression. If InputTerm is an expression, then it will be evaluated before the
conversion.
The convert predicate performs a clean and genuine conversion of the given InputTerm, returning a
new term ReturnTerm of the specified new domain returnDomain. If convert cannot perform the
required conversion, it rises errors. The similar functionality is provided by the tryConvert/2->
predicate, but tryConvert-> fails and does not produce any runtime errors if it cannot perform the
conversion.
Allowed conversions
l
l
l
Language Reference
l
l
l
l
l
l
Synonyms of domains are converted using the same rules that are applied to the domains
themselves.
Numerical domains can be converted to the numerical domains only.
Integral constants are the representatives of the anonymous integral domain: [const .. const].
Real constants are the representatives of the anonymous real domain: digits dig [const ..
const], where dig is the number of the digits in mantissa without insignificant zeroes.
A value of the symbol domain can be converted to the string domain and vice versa.
A value of binary domain can be converted to the pointer domain.
The domains that are implicitly introduced for interfaces can be converted only to the interface
domains according to the rules specified below.
All other domains cannot be converted.
The range is considered first during such conversion. If the ranges of source and target do not
intersect, then an error is produced. If the ranges of source and target only partially intersect,
then run-time checking is generated. Also, if one of domains is real and another is an integral
one, then the integer range is converted to the real range before the comparison.
When input term in real and output is integer, then convert/2-> and tryConvert/2->
predicates truncate the input value to the nearest integer value, which is nearer to zero.
If object is created by class, which implements x interface, and then object is passed as parameter
of type a to some predicate, then it is allowed to convert the object to b type.
Exceptions:
Language Reference
digitsOf
digitsOf : (<real-domain> Domain) -> unsigned.
The input parameter domainName of this compiling-time predicate is a floating-point domain, it should
be explicitly specified at compile-time (that is, domainName cannot come from a variable). The
predicate returns the number Precision that was determined by the digits attribute in the domain
declaration.
The compiler guarantees that values of the domain domainName will have at least Precision number
of significant decimal digits.
errorExit
errorExit : (unsigned ErrorNumber) erroneous.
Performs a run-time error with the specified return code ErrorNumber, which can be used in the trycatch-finally.
fail
fail : () failure.
The fail predicate forces failure and, hence, always causes backtracking. A clause that fails (with fail
or for some other reason) cannot bind output arguments.
free
free : (<variableName> Variable) determ.
The free predicate succeeds if the specified Variable is free and fails if Variable is bound. The free
Language Reference
This predicate creates the list of terms of the universal type any from the EllipsisBlock ... (i.e. from
the special varying parameters block).
Call-template for this function is:
AnyTermList = fromEllipsis(EllipsisBlock )
The only effect of the call is that the Variable will be restricted to the type Type.
The variable can be free, bound or of some mixed flow and the binding of the variable will not change
in any way.
The function form is called with a type as first argument and a value as second argument, and it
returns the same value.
hasDomain : (<type> Type, Type Value) -> Type Value.
The only effect of the call is to ensure that the Value will be restricted to the type Type.
lowerBound
lowerBound : (<numeric-domain> NumericDomain) -> <numeric-domain> LowerBound.
Language Reference
The lowerBound is a compiling-time predicate. The lowerBound returns the lower bound value
LowerBoundValue of the specified numeric domain domainName. The return value
LowerBoundValue belongs to the same domain domainName. The domainName parameter should be
the name of any numerical domain; this domain name should be explicitly specified at compile-time
(that is, domainName cannot come from a variable). See also upperBound/1->.
It will give a compile time error if the specified domain domainName is not numeric domain.
in
See in/2.
isErroneous
isErroneous : (<fact-variable> FactVariable) determ.
The predicate succeeds if the specified fact variable factVariableName has the erroneous value,
otherwise it fails.
maxDigits
maxDigits : (<real-domain> RealDomain) -> unsigned MaxDigits
Retrieves the value of digits (precision) of the basic domain corresponding to the specified floatingpoint domain domainName.
Call-template for this function is:
MaxDigitsNumber = maxdigits(domainName)
The return maximal number of digits MaxDigitsNumber for the domainName parameter, which should
be the name of a real domain.
not
See not.
or
See or (;).
Language Reference
This predicate returns the name PredicateFullName of the predicate in which it is invoked. The
returned predicate name is qualified with a scope name.
predicate_fullname can only be used inside a clause. Use of predicate_fullname in other places
causes a compile time error. See also predicate_name.
predicate_name
predicate_name : () -> string PredicateName.
This predicate returns the name PredicateName of the predicate in which it is invoked.
predicate_name can only be used inside a clause. Use of predicate_name in other places causes
a compile time error. See also predicate_fullname
programPoint
programPoint : () -> core::programPoint ProgramPoint.
This predicate returns the name programPoint corresponding to the place where it is invoked.
retract
retract : (<fact-term> FactTerm) nondeterm anyflow.
Successively removes the first matching fact from the facts database. Fails when no more facts
match.
Call-template for this predicate is:
retract(FactTemplate)
Here FactTemplate should be a fact term. The retract/1 predicate deletes the first fact that
matches the FactTemplate in the appropriated facts database. During backtracking, the rest of the
matching facts will be deleted.
Notice that FactTemplate can have any level of instantiation. The FactTemplate is matched with
the facts in the facts database, which means that any free variables will be bound in the call to
retract/1.
Language Reference
will retract the first matched person fact that has "Hans" as the first argument and anything as the
second argument.
When retracting a fact, which is declared to be determ, the call to retract/1 will be deterministic.
See also retractall/1 and retractFactDb.
The retract/1 predicate cannot be applied to single facts or fact variables.
Be careful calling retract/1 with free FactTemplate variable if any single fact is declared in the
project current scope. If you retract a single fact, then the run-time error is generated. The
retract/1 predicate fails when there are no more matches.
retractall
retractall : (<fact-term> FactTerm) .
Language Reference
The retractFactDb/1 removes all facts from the named facts database FactDB.
Notice, it is impossible to retract single facts and fact variables, so the predicate leaves such ones
as they are.
See also retractall/1 and retract/1.
retractAll/2
Obsolete predicate! Use retractFactDb/1 instead.
sizeBitsOf
sizeBitsOf : (<domain> DomainName) -> unsigned BitSize.
Retrieves the number of bits occupied in memory by an entity of the specified domain DomainName.
Call-template for this function is:
BitSize = sizeBitsOf(DomainName)
This compiling-time predicate receives the domain DomainName as input parameter and return the
size of memory that is occupied by the entity of the given domain. The result is measured in bits. For
the integer domains sizeBitsOf/1-> predicate returns the value that was defined for the size-field in
a domain's declaration.
The following is always true for the integral domains:
sizeOfDomain(domain)*8 - 7 <= sizeBitsOf(domain) <= sizeOfDomain(domain)*8
Retrieves the number of bytes occupied in memory by the specified term Term.
Call-template for this function is:
ByteSize = sizeOf(Term)
Language Reference
The sizeOf/1-> function receives a term as input parameter and returns value ByteSize that
specifies the number of bytes occupied in memory by this term Term.
sizeOfDomain
sizeOfDomain : (<domain> Domain) -> integer ByteSize.
Retrieves the number of bytes occupied in memory by the entity of the specified domain
DomainName.
Call-template for this function is:
ByteSize = sizeOfDomain(DomainName)
This compiling-time predicate receives the domain DomainName as input parameter and return the
size of memory that is occupied by the entity of the given domain. The result is measured in bytes.
The returned value ByteSize belongs to the integer domain. Compare with sizeBitsOf/1->, which
returns size of a domain measured in bits.
sourcefile_lineno
sourcefile_lineno : () -> unsigned LineNumber.
Returns the current line number in the source file processed by the compiler.
sourcefile_name
sourcefile_name : () -> string FileName.
Returns a string that represents the date and time of the currently compiled source file in format
YYYY-MM-DD HH:MM:SS. Where:
l
l
l
l
l
l
YYYY - Year.
MM - Month.
DD - Day.
HH - Hour.
MM - Minute.
SS - Second.
succeed
Language Reference
Converts the specified Term to the value of universal term type any.
Call-template for this function is:
UniversalTypeValue = toAny(Term)
toBinary
toBinary : (Term) -> binary Serialized.
When a Term (of some domain domainName) is converted into a binary, it can safely be stored in a
file or sent over a network to another program. Later the obtained binary value Serialized can be
converted back to a Visual Prolog term, using toTerm/1-> function (the domain for the reversed term
should be adequate to domainName) for the reverse conversion.
toBoolean
toBoolean : (<term> SubGoal) -> boolean Succeed.
The purpose of this meta-predicate is to convert the deterministic call (to a predicate or fact) to the
procedure that returns the value of boolean domain.
Call-template for this meta-predicate is:
True_or_False = toBoolean(deterministic_call)
The toBoolean/1-> meta-predicate returns boolean value. The result is true if deterministic_call
succeeds. The result is false if deterministic_call fails.
toEllipsis
Language Reference
This predicate creates EllipsisBlock ... (i.e. the special varying parameters block) from the list of
terms of the universal type any. Such EllipsisBlock can be later passed to a predicate which expects
the varying number of arguments (i.e. is declared with the ellipsis (...)), like write/..., at the position
of the ellipsis (...).
Call-template for this function is:
EllipsisBlock = toEllipsis(<any_term_list>), write(EllipsisBlock )
When a Term (of some domain domainName) is converted into a string, it can safely be stored in a
file or sent over a network to another program. Later the obtained string value can be converted
back to a Visual Prolog term, using toTerm/1-> function (the domain of the return value should be
adequate to domainName) for the reverse conversion.
toTerm
toTerm
toTerm
toTerm
toTerm
toTerm
toTerm
Converts the string/binary/any representation of the specified term Serialized into representation
corresponding to the domain of Term variable of the return value. The domain can be stated
explicitly or it can be left to the compiler to determine a suitable domain.
Call-template for this function is:
Term = toTerm(Serialized) % with implicit domain
Term = toTerm(domainName, Serialized) % with explicit domain, domainName
If the domain is not specified the compiler must be able to determine the domain for the returned
value Term at compile-time. Notice that binary version of toTerm predicate performs almost byte to
byte conversion and only checking general compatibility of Serialized data with the domain required
Language Reference
Run time errors are generated when the toTerm predicate cannot convert the string or binary
into a term of the specified domain.
tryToTerm
tryToTerm
tryToTerm
tryToTerm
tryToTerm
tryToTerm
tryToTerm
Converts the string/binary/any representation Serialized into a term Term like toTerm. The only
difference between the predicates is that tryToTerm fails if it cannot convert the string or binary or
any into a term of the specified domain whereas toTerm raises an exception.
See also toTerm.
tryConvert
tryConvert : (<type> Type, Value) -> <type> Converted determ.
Checks whether the input term InputTerm can be strictly converted into the specified domain Type
and returns the converted term Converted.
Call-template for this function is:
ReturnTerm = tryConvert(returnDomain, InputTerm)
Arguments:
l
Language Reference
InputTerm: Specifies the term that must be converted. InputTerm may be any Prolog term or
an expression. If InputTerm is an expression, then it will be evaluated before conversion.
ReturnTerm: Returned term ReturnTerm will be of returnDomain domain.
The conversion rules are the same as of the embedded predicate convert/2->, but tryConvert/2->
fails when convert/2-> generates conversion errors.
This predicate succeeds if the corresponding conversion succeeds. Otherwise it fails. The
tryConvert/2-> predicate tries to perform a clean and genuine conversion of the given InputTerm
into a value of the specified domain returnDomain. The tryConvert/2-> predicate will fail if the
required conversion cannot be performed. When tryConvert/2-> predicate succeeds, it returns the
term ReturnTerm converted to the specified domain returnDomain.
For allowed conversions and rules of checked explicit conversions see convert/2-> predicate.
See also uncheckedConvert/2->.
uncheckedConvert
uncheckedConvert : (<type> Type, Value) -> <type> Converted.
Arguments:
l
Language Reference
The upperBound is a compiling-time predicate. The upperBound returns the upper bound value of
the specified numeric domain domainName. The return value UpperBound belongs to the same
domain domainName. The domainName parameter should be the name of any numerical domain; this
domain name should be explicitly specified at compile-time (that is, domainName cannot come from a
variable).
See also lowerBound/1->.
Will cause a compile time error if the specified domain domainName is not numeric domain.
Language Reference
This compiler directive uses "include the first file occurrence only" semantics. That is, if a compilation
unit contains several include directives for the same file, it will be included only one time with the first
include directive.
Each included file must contain several accomplished scopes; an included file cannot contain
uncompleted scopes. That is, it should contain several accomplished interface declarations, class
declarations, class implementations or/and several compiler directives.
The compiler tries to find the specified include source file in the following way:
1. If the filename contains an absolute path, then this file should be included.
2. Otherwise, the compiler searches for the specified include filename among the paths that had
been defined by the /Include command line option. These paths are handled consequently as
they are specified in the option. In the IDE you can set these paths in the Include Directories
in the Directories tab of the Project Settings dialog.
If the compiler does not find the specified file a compiling time error is generated.
This directive can be used in any places where binary constants are allowed. The string_literal should
specify an existing filename. The syntax is the same as in the #include compiler directive described
in the previous paragraph Source File Inclusions. The compiler tries to find the specified file in the
same way as for #include compiler directive.
The typical usage is like this:
constants
myBin : binary = #bininclude("Bin.bin").
% Creates value of binary constant from "Bin.bin" file
When creating a binary constant the compiler adds the EOS symbol immediately after this constant,
which makes safe the directive usages like this:
constants
text : string = uncheckedConvert(string, #bininclude("text.txt")).
% Here text.txt is a text file, which is normally not text zero terminated
These compiler directives are applied only to classes classNames, which do not construct objects.
They can be used only outside scopes; that is, they cannot be used inside declarations of interfaces
and classes and they cannot be used inside implementations of classes.
By default, predicates within one executed module are hidden at runtime for all other executed
modules. An #export compiler directive makes names of specified classes public outside the module
in which they are declared (and implemented). Therefore, all predicates from this module declared in
the classes (specified in an #export directive) become accessible while runtime from other executed
modules.
Usually, an #export compiler directives can be used in projects, which target modules are DLLs. It
enumerates classes declared in a DLL, which should be accessible to other modules that use this DLL.
If a compilation unit export some class, then this compilation unit should contain this class
implementation.
Also an #export compiler directives can be used to specify condition expressions for #if compiler
directives.
For example, let us suppose that somewhere in the beginning of a compilation unit the compiler has
Language Reference
Then the compiler, if in the subsequent code it meets an #if compiler directive with the same
#export compiler directive used as the condition expression, for example like this:
#if #export className #then ... #endif
Then the compiler evaluates the #export condition expression as true and, hence, the compiler
executes the #then branch of the conditional compilation directive.
For example, the following #export compiler directive with the subsequent #if conditional compilation
compiler directive:
#export className
...
#if #export className #then #requires "some.pack" #endif
guaranty that the "some.pack" package will be included into the compilation unit.
From the other hand, if an #export compiler directive is not met by the compiler (somewhere in the
compilation unit before the #if compiler directive, which uses the same #export compiler directive as
the conditional expression), then the compiler evaluates this #export condition expression as false.
Hence, the compiler will not execute the #then branch of the #if conditional compilation directive.
That is, the single #if compiler directive without previous #export directive
#if #export className #then #requires "some.pack" #endif
When the compiler meets any of these directives, it generates the correspondent warning message
and place the directive text into a listing file.
A listing file name can be specified with the compiler directive:
/listingfile:"FileName"
Notice that no empty spaces can be used between the colon : (after /listinglile) and "FileName".
By default the compiler does NOT generate informative messages for the #message, #requires, and
#orrequires directives. You can switch generation of these informative messages ON specifying the
compiler options:
/listing:message
/listing:requires
/listing:ALL
C:\Tests\test\test.pro(14,10):informationc062:#message"Somemessage"
The directive #requires (#orrequires) issues arbitrary user-defined messages about the needed
source (object) files into a listing file. The #orrequires directive cannot be used alone: the
#requires directive should immediately (separated with white spaces or comments only) precede it.
The directive #error always terminates the compilation and issues the user-defined error message
like the following into a listing file:
C:\Tests\test\test.pro(14,10):errorc080:#error"Compilationisinterrupted"
You can parse and analyze these messages and accept the required actions. For example, the VDE
analyzes information printed by the #requires and #orrequires directives and automatically adds all
needed PFC packages and standard libraries to the compiled project (See also the Handling Project
Modules topic).
Language Reference
Otherwise the compiler generates the error message for invalid option. If there are several #options
directives they are handled in the textual order.
Conditional Compilation
The conditional programming constructions are part of the Visual Prolog language. Only other compiler
directives, Compilation Units, and Program Sections (including empty) can be conditional. The
following syntax is used:
ConditionalItem :
#if Condition #then CompilationItem-list-opt ElseIfItem-list-opt ElseItem-opt #endif
ElseIfItem :
#elseif Condition #then CompilationItem
ElseItem :
#else CompilationItem
Here Condition can be any expression, which can be evaluated to fail or succeed during compilation
time.
Each one conditional compilation statement must be in one file, that is, the compiler directives #if,
#then, #elseif and #else (if present), #endif of the same level of nesting must be in one file.
During compilation the compiler evaluates the conditions, in order to determine which parts to include
in the final program. Parts that are excluded from the final program are called the dead branches.
All branches of conditional compilation items are syntax checked and must be syntactically correct.
That is, also the dead branches must be syntactically correct.
The compiler only calculates conditions on a need to know basis, i.e. it does not calculate conditions
in dead branches.
A condition may not depend on any code, which is located textually inside the conditional statement.
The example below is illegal because the condition depends on the scope (and constant) which is
declared inside the condition branch.
#if aaa::x > 7 #then % ERROR!
class aaa
constants
x=3
end class aaa
#else
class aaa
constants
x = 23
Language Reference
Language Reference
Syntax
Attributes :
[ Attribute-comma-sep-list ]
Attribute : one of
LowerCaseIdentifier
LowerCaseIdentifier ( Literal-comma-sep-list )
Insertion Points
The attributes of interfaces, classes and implementations are right after the scope qualifications.
InterfaceDeclaration :
interface IinterfaceName
ScopeQualifications
Attributes-opt
Sections
end interface IinterfaceName-opt
ClassDeclaration :
class ClassName ConstructionType-opt
ScopeQualifications
Attributes-opt
Sections
end class ClassName-opt
ClassImplementation :
implement ClassName
ScopeQualifications
Attributes-opt
Sections
end implement ClassName-opt
The attributes of constants, domains, predicates, properties and facts are at the end (i.e. right
before the terminating dot).
ConstantDefinition: one of
ConstantName = ConstantValue Attributes-opt
ConstantName : TypeName = ConstantValue Attributes-opt
DomainDefinition:
DomainName FormalTypeParameterList-opt = TypeExpression Attributes-opt
Language Reference
PredicateDeclaration :
PredicateName : PredicateDomain LinkName-opt Attributes-opt
PredicateName : PredicateDomainName LinkName-opt Attributes-opt
PropertyDeclaration :
PropertyName : PropertyType FlowPattern-list-opt Attributes-opt
FactDeclaration :
FactVariableDeclaration Attributes-opt
FactFunctorDeclaration Attributes-opt
Specific Attributes
byVal
An argument is transferred directly on the stack rather than using a pointer. Valid for formal predicate
arguments provided the language is stdcall, apicall or c.
Example
predicates
externalP : (point Point [byVal]) language apicall.
deprecated
The declared entity is deprecated. The string literal describes how to migrate from it. The entity still
exist, but usage will cause a warning. The entity will not exist in future versions of Visual Prolog. Valid
for member declarations and scopes.
Example
predicates
oldFasioned : (string Arg) [deprecated("Use newFasion instead")].
formatString
The argument is a format string for a subsequent ellipsis argument (i.e. ...). Valid for one string
argument of a predicate with an ellipsis argument. The use of formatString will make the compiler
check the validity of actual arguments with respect to actual format strings (where possible).
Example
Language Reference
in
The argument is an input argument. Valid for a formal predicate argument.
Example
predicates
pred : (string InputArg [in]).
inline
inline alters the memory layout of a struct (i.e. a single alternative functor domain with an align
qualification). The purpose of the attribute is to ease interfacing to foreign languages and should
normally not be used for pure Visual Prolog.
inline can be used in three cases:
l
l
l
When using inline on struct field in another struct its data will be inlined instead of having a pointer
to the struct
Example
domains
point =p(integer X, integer Y).
domains
rectangle =
r(
point UpperLeft [inline],
point LowerRight [inline]
).
Since UpperLeft and LowerRight are inlined the struct have the same memory layout as this one:
domains
rectangle2 =
r2(
integer UpperLeft_X,
integer UpperLeft_Y,
integer LowerRight_X,
integer LowerRight_Y
).
When using inline(<size>) on a string or a string8 field in a struct, the struct will contain a fixed
size string with <size> characters (i.e. char or char8, respectively). The strings will be zero
Language Reference
DeviceName is an inlined Unicode string of length 12. The struct have the same layout as:
domains
device =
device(
integer Id,
char DeviceName_01,
char DeviceName_02,
char DeviceName_03,
char DeviceName_04,
char DeviceName_05,
char DeviceName_06,
char DeviceName_07,
char DeviceName_08,
char DeviceName_09,
char DeviceName_10,
char DeviceName_11,
char DeviceName_12
).
When using inline(<size>) on the pointer type the struct will contain <size> bytes, and the
pointer will become a pointer to that field:
Example
domains
mark =
mark(
integer Position,
pointer Data [8]
).
Data is pointer to 8 inlined bytes The struct have the same layout as:
domains
mark2 =
mark2(
integer Position,
byte Data_1,
byte Data_2,
byte Data_3,
byte Data_4,
byte Data_5,
byte Data_6,
byte Data_7,
byte Data_8
).
Language Reference
out
The argument is an output argument. Valid for a formal predicate argument.
Example
predicates
pred : (string OutputArg [out]).
programPoint
Used for a predicate or constructor declaration to indicate that it recieves an extra input argument
which describes the place in a program (program point) where this predicate was called. This
additional argument has programPoint type which is declared in the PFC core class like:
domains
programPoint = programPoint(hScopeDescriptor ClassDescriptor, string PredicateName, sourceCursor
The clause name of such predicate or constructor should have suffix "_explicit". Valid for a predicate
or constructor declaration.
Example
predicates
pred : (string String) [programPoint].
clauses
pred_explicit(ProgramPoint, String) :...
The programPoint attribute is described in more details in the description of Predicates, and it is a
vital part of the exception system, see Exception Handling.
retired
Language Reference
sealed
Used for an interface to indicate that it cannot be supported by any other interface. This allows to
create more efficient codes, because the compiler provides some optimization when using the objects
of such type. Valid for an object creating class declaration as a construction interface.
Example
interface myInterface
[sealed]
...
end interface myInterface
union
Used for creating functor domains with several alternatives but no real functors. This should only be
used to mimic C/C++ union structs in low-level interfacing. Valid for functor domain with several
alternatives and alignment.
Example
domains
u64var = align 4
u64(unsigned64 Value64);
u64_struct(unsigned Low32, unsigned High32)
[union].
used
An unused local member can be marked used to prevent the compiler to issue a warning and remove
the corresponding code. Valid for members.
Example
predicates
seeminglyUnused : () [used].
Language Reference