Language Reference/Generic Interfaces and Classes

From wiki.visual-prolog.com

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:

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.

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

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*}
    ...

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

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

Generic Implmentations

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