Language Reference/Generic Interfaces and Classes

Interfaces and classes can be parametrized with type parameters so that they can be used in different instantiations in different contexts.

This section must be considered as an extension to the individual sections about:
 * 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

queueClass{@Elem} constructs objects type queue{@Elem} for any instantiation of @Elem.

The implementation can look like this:

implement queueClass{@Elem} facts queue_fact : (@Elem Value). clauses insert(Value) :- assert(queue_fact(Value)). clauses tryGet = Value :- retract(queue_fact(Value)), !. end implement queueClass

This piece of code illustrates how to create an integer queue and insert an element in it:

..., Q = queueClass{integer}::new, Q:insert(17), ...

It is not necessary to apply the type explicitly, instead the compiler can infer it from the context:

..., Q = queueClass::new, Q:insert(17), ...

The compiler sees that Q must be an integer queue, because we insert 17 into it.

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):

interface xxx % xxx/0 ... end interface xxx

interface xxx{@A, @B} % error: Several classes, interfaces and/or namespaces have the same name 'xxx' ... end interface xxx

Parameters can be used in supported interfaces:

interface xxx{@P} supports yyy{@P} % legal: @P is bound ... end interface xxx

interface xxx supports yyy{@P} % illegal: @P must be bound ... 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*} ...

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>

The construction type must be generic (i.e. a generic interface).

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:

class xxx{@A} : xxx{@A} ... end class xxx % no parameters here

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

Parameters in the construction type, etc must be bound:

class bbb : xxx{@Q} % error: Free parameter '@Q' is used in type expression ...

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

Syntax
Generic implementations have a list of type parameters.

<ClassImplementation> : implement <ClassName> { <ScopeTypeVariable>-comma-sep-list-opt } <ScopeQualifications> <Sections> end implement <ClassName>-opt

<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