Skip to content
wangp edited this page Aug 20, 2017 · 1 revision

An abstract type is a type whose representation is hidden from its users. This is useful to prevent code inadvertently depending on the representation, allowing you to change the representation later without breaking that code. Another reason to use abstract types is if the data representation requires certain invariants to be maintained. By restricting the code which can work directly on the underlying representation, you limit the code that might accidentally break the invariant required by that representation, and the amount of code that needs to be inspected if something goes wrong.

In Mercury, an abstract type is declared in the interface section of a module, then defined in the implementation section of the module. The definition may be of a discriminated union type or an equivalence type.

Example:

:- module mytree.
:- interface.

    % Declaration of an abstract type.
    %
:- type mytree(T).

:- pred init(mytree(T)::out) is det.

:- pred insert(T::in, mytree(T)::in, mytree(T)::out) is semidet.

:- pred contains(mytree(T)::in, T::in) is semidet.

:- implementation.

    % Definition of the type.
    %
:- type mytree(T)
    --->    nil
    ;       node(mytree(T), T, mytree(T)).

% implementation of predicates omitted

Other modules can import the module and see that the type mytree(T) exists. The data constructors nil/0 and node/3 are not exported, so other modules cannot construct or deconstruct mytree(T) terms directly. They can only work with values of mytree(T) using the the predicates and functions provided by the mytree module, or one of its sub-modules. (By the visibility rules for sub-modules, any descendant module of mytree would have details of the type representation of mytree(T).)

Note that code in other modules will still able to compare two terms of an abstract type for equality, or compare them with the compare/3 predicate. Also, it is possible to construct and deconstruct terms without access to the type's data constructors, using the construct and deconstruct library modules. As an example, the io.write family of predicates write terms by deconstructing them at runtime, which includes terms of abstract types. The io.read family of predicates is able to construct terms of a given type at runtime, including terms of abstract types.

Clone this wiki locally