Must_be/2 and is_of_type/2 types

The documentation for must_be/2 shows most of the types but some are missing.

The source code for has_type/2 shows the complete set.

has_type(any, _).
has_type(atom, X)         :- atom(X).
has_type(atomic, X)       :- atomic(X).
has_type(between(L,U), X) :- 
    (
        integer(L)
    ->
        integer(X), between(L,U,X)
    ;
        number(X), X >= L, X =< U
    ).
has_type(boolean, X)               :- (X==true;X==false), !.
has_type(callable, X)              :- callable(X).
has_type(char,  X)                 :- '$is_char'(X).
has_type(code,  X)                 :- '$is_char_code'(X).
has_type(chars, X)                 :- '$is_char_list'(X, _Len).
has_type(codes, X)                 :- '$is_code_list'(X, _Len).
has_type(text, X)                  :- text(X).
has_type(compound, X)              :- compound(X).
has_type(constant, X)              :- atomic(X).
has_type(float, X)                 :- float(X).
has_type(ground, X)                :- ground(X).
has_type(cyclic, X)                :- cyclic_term(X).
has_type(acyclic, X)               :- acyclic_term(X).
has_type(integer, X)               :- integer(X).
has_type(nonneg, X)                :- integer(X), X >= 0.
has_type(positive_integer, X)      :- integer(X), X > 0.
has_type(negative_integer, X)      :- integer(X), X < 0.
has_type(nonvar, X)                :- nonvar(X).
has_type(number, X)                :- number(X).
has_type(oneof(L), X)              :- ground(X), \+ \+ memberchk(X, L).
has_type(proper_list, X)           :- is_list(X).
has_type(list, X)                  :- is_list(X).
has_type(list_or_partial_list, X)  :- is_list_or_partial_list(X).
has_type(symbol, X)                :- atom(X).
has_type(var, X)                   :- var(X).
has_type(rational, X)              :- rational(X).
has_type(string, X)                :- string(X).
has_type(stream, X)                :- is_stream(X).
has_type(encoding, X)              :- current_encoding(X).
has_type(dict, X)                  :- is_dict(X).
has_type(list(Type), X)            :- is_list(X), element_types(X, Type).
has_type(type, Type)               :- ground(Type), current_type(Type,_,_).

Additional types can be added.

When using :- multifile (multifile/1) the additional types can be in more than one file including the code being developed.

Here is an example of adding the type windows_directory

:- multifile
    error:has_type/2.

error:has_type(windows_directory,Directory) :-
    exists_directory(Directory).

Use from top-level

?- is_of_type(windows_directory,'C:').
true.

?- is_of_type(windows_directory,'A:').
false.

?- must_be(windows_directory,'C:').
true.

?- must_be(windows_directory,'A:').
ERROR: Type error: `windows_directory' expected, found `'A:'' (an atom)
ERROR: In:
ERROR:   [13] throw(error(type_error(windows_directory,'A:'),_59734))
ERROR:    [9] <user>
ERROR: 
ERROR: Note: some frames are missing due to last-call optimization.
ERROR: Re-run your program in debug mode (:- debug.) to get more detail.

Example unit tests.

:- begin_tests(has_type).

test(001) :-
    is_of_type(windows_directory,'C:').

test(002,[fail]) :-
    is_of_type(windows_directory,'A:').

test(003) :-
    must_be(windows_directory,'C:').

test(004,[error(type_error(windows_directory,'A:'),_)]) :-
    must_be(windows_directory,'A:').

:- end_tests(has_type).

Personal note:

I tend to think of must_be/2 as being used with code that simulates an imperative style and throws an exception when it fails, while I tend to think of is_of_type/2 as being used with code that is logical and fails instead of throwing an exception. Also if the check is part of a multi-clause predicate, then be careful about using must_be/2 when is_of_type/2 in needed.

The reason I take the time to convert such simple predicates into the has_type/2 convention is so that they can be easily identified as guard statements when at the start of a predicate. In production code these predicates can get more complicated and this really does make the code easier to comprehend.

See: “The Craft of Prolog” by Richard A. O` Keefe (WorldCat), Section 3.10 Pruning Alternative Solutions

Also of interest:
Package mavis - The mavis module (because she helps with typing :wink: ) allows one to use optional type declarations in Prolog code.


Posted here because I am always looking this up.

Made a wiki so it can be edited by others in the future if needed.

1 Like