A domain section defines a set of domains in the current scope (see Interface, Class Declaration, and Class Implementation).
DomainsSection: domains DomainDefinition-dot-term-list-opt
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 subdomain of the domain denoted by the domain 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:
A domain expression denotes a type in a domain definition:
DomainExpression: one of TypeName CompoundDomain ListDomain PredicateDomain IntegralDomain RealDomain TypeVariable ScopeTypeVariable TypeApplication
The full range of DomainExpression s can only be used in a domain definition. TypeExpression is a subset of these expressions that are used in other many other contexts.
TypeExpression: one of TypeName ListDomain TypeVariable ScopeTypeVariable TypeApplication
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: one of 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.
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 (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
Alignment: align IntegralConstantExpression
Here IntegralConstantExpression is an expression, which must be compile time evaluated to an integral value.
A compound domain declaration declares a list of functor alternatives with optional alignment. Alignment must be 1 , 2 or 4 .
If a compound domain consists of one functor alternative, then it is considered as structure and has representation, which is binary compatible with the appropriate structure in language C.
FunctorAlternative: FunctorName FunctorName ( FormalArgument-comma-sep-list-opt )
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.
domains t1 = empty(); cons(integer V, t1 Tail).
t1 is a compound domain with two alternatives. The first alternative is the null-ary functor empty ( ) , while the second alternative is the two-ary functor cons, 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:
empty cons(77, empty()) cons(33, cons(44, cons(55, empty())))
In the example above we used parenthesis after the null-ary function empty. Such parenthesis are optional in all situations, except in a domain definition consisting only of a single null-ary functor. In that case parenthesis are required to distinguish it from a synonym/subtype definition.
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.
domains t1 = nil; subt2(t2). t2 = hh(t1, t1).
t1 is a compound domain with two alternatives. The first alternative is the null-ary functor nil, while the second alternative is the unary functor subt2, which takes a term of the domain t2 as argument. t2 is a compound domain with one alternative the functor hh, which takes two t1 terms as argument. So the domains t1 and t2 are mutually recursive.
The following are terms in the domain t1:
nil subt2(hh(nil, nil)) subt2(hh(subt2(hh(nil, nil)), nil)) consg(hh(nil, g(hh(nil, nil)))) subt2(hh(subt2(hh(nil, nil)), subt2(hh(nil, nil))))
List domains represent sequences of values of a certain domain. Thus, all elements in a T list must be of type T .
ListDomain: TypeExpression *
T * is the type of lists of T elements.
The following syntax is used for lists:
ListExpression: [ Term-comma-sep-list-opt ] [ Term-comma-sep-list | Tail ]
Tail: Term
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 mix-fix 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 , . , En | L ] is shorthand for [ E1 | [ E2 | [ . [ En | L ] . ] ] ]
[ E1 , E2 , . , En ] is shorthand for [ E1 , E2 , . , En | [ ] ] , which in turn is shorthand for [ E1 | [ E2 | [ . [ En | [ ] ] . ] ] ] .
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.
The details concerning predicate domains are described in Predicate Domains.
But notice that predicate domains that are used in domain definitions (in a domains section) can at most state one flow.
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
If a DomainName is stated in front of the IntegralDomainProperties , then this domain must itself be an integral domain and the resulting domain will be child-type (i.e. subtype) of this domain. In that case IntegralDomainProperties may not violate the possibility of being a subtype, i.e. the range cannot be extended and the size cannot be changed.
IntegralDomainProperties: IntegralSizeDescription IntegralRangeDescription-opt IntegralRangeDescription IntegralSizeDescription-opt 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 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).
RealDomain: DomainName-opt RealDomainProperties
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
Here RealConstantExpression is an expression, which must be compile time evaluated to a floating point value. That is the real domain precision and limits must be calculated while compiling time.
The real range description declares minimal and maximal limits for the real domain. If a limit is omitted then it will be the same as for the parent domain. If there is no parent domain then the largest possible range for the precision will be used.
Notice that the specified minimum value should not exceed the specified maximum value. That is:
MinimalBoundary MaximalBoundary
This section contains the formal syntax for generic domains, for a more complete introduction to generics please see the tutorial Objects and Polymorphism and the section Generic Interfaces and Classes.
FormalTypeParameterList: TypeVariable-comma-sep-list-opt
A formalTypeParameterList is a list of typeVariables
TypeVariable: UpperCaseIdentifier
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.
Visual Prolog uses some internal types, called root types and universal types.
A number literal like 1 does not have any particular type, it can be used as a value of any type that contains 1 , including real types.
We say that 1 have a universal type. Having a universal type means that it have any type, which can represent its value.
Arithmetic operations also return universal types.
Arithmetic operations are very liberal with their operand requirements: You can add integers of any integer domain with each other.
We say that arithmetic operands takes root types as arguments. The integer root type is super-type of any integer type (regardless that it is not mentioned in their declarations). Hence any integer type can be converted to the integer root type, and, since the arithmetic operations exist for the root types, it means one of them will work on any integer domains.
The actual number of root types and which operands exist is a matter of library facilities, and outside the scope of this document to describe.