# `types`

This library implements predicates over standard Prolog term types and also terms representing common data structures such as lists and pairs.

It also includes a user-extensible `type` object defining type checking predicates over common Logtalk and Prolog term types. The types define a hierarchy with the Prolog type `term` at the root (i.e., type-checking a predicate argument of type `term` trivially succeeds). Some types are only meaningful for backend Prolog systems supporting non-universal features (e.g., `cyclic` or `char(CharSet)` with a Unicode character set). See the API documentation for a full list of the types defined by default.

## API documentation

Open the ../../docs/library_index.html#types link in a web browser.

## Loading

To load all entities in this library, load the `loader.lgt` file:

    | ?- logtalk_load(types(loader)).

If your code only requires the most basic types, you can load in alternative the file:

    | ?- logtalk_load(basic_types(loader)).

See the notes on the `basic_types` virtual library for details.

## Testing

To test this library predicates, load the `tester.lgt` file:

    | ?- logtalk_load(types(tester)).

## Type-checking

This library `type` object can be used to type-check common Logtalk and Prolog term types (see the object documentation for a listing of all the pre-defined types). The `valid/2` predicate succeeds or fails if a term is of a given type. For example:

    | ?- type::valid(positive_integer, 42).
    yes

    | ?- type::valid(positive_integer, -13).
    no

The `check/2` and `check/3` predicates throw an exception if a term is not of a given type. For example:

    | ?- catch(type::check(integer, abc), Error, true).
    Error = type_error(integer, abc)
    yes

If we require a standard `error/2` exception term, the `check/3` predicate takes a context argument. For example:

    | ?- catch(type::check(integer, abc, foo/3), Error, true).
    Error = error(type_error(integer, abc), foo/3)
    yes

Typically, the context is provided by calling the built-in `context/1` method.

## Defining new types

To define a custom type, define clauses for the multifile predicates `type::type/1` (to declare the type) and `type::check/2` (to type-check values). For example:

    :- multifile(type::type/1).
    type::type(age).

    :- multifile(type::check/2).
    type::check(age, Term) :-
        type::check(between(non_negative_integer, 0, 150), Term).

Be careful to ensure that the new type definitions don’t introduce spurious choice-points for these predicates. The unit tests of the `types` library perform this check for pre-defined and loaded user-defined ground types.

When defining a meta-type (i.e., a type with arguments that are also types), add also a clause for the `type::meta_type/3` multifile predicate. For example:

    :- multifile(type::meta_type/3).
    type::meta_type(tuple(Type1, Type2, Type3), [Type1, Type2, Type3], []).

This predicate is called when checking if a type is a defined type. For meta-types, that check must extend to the sub-types.

## Examples

See e.g. the `os` library implementation of custom types for files and directories. Or the `expecteds` and `optionals` libraries custom types. See also the `my_types` programming example.