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