Categories
Mastering Development

Testing types of Prolog terms: slight problems

Here is some code to test the type/metatype of SWI-Prolog terms:

% var,nonvar are meta-question about the state of computation, not about the term
% Note: X can be a variable (if it is fresh) or a nonvar (if it is set)
%       but x can ever only be a nonvar

typeof(X, var)           :- var(X),!.     % if it's a variable, we don't look for any other match
typeof(X, nonvar)        :- nonvar(X).    % nonvar(X) is the complement of var(X)

typeof(X, int)           :- integer(X).   % is there a way to distinguish "machine ints" from "bigints"? 
typeof(X, float)         :- float(X).     % IEEE 754 floats; magnitude: always double (what if long doublr arrives?)
typeof(X, number)        :- number(X).    % rationals are not (yet) numbers
typeof(X, rational)      :- rational(X).  % covers "big decimals" too

typeof(X, atom)          :- atom(X).
typeof(X, atomic)        :- atomic(X).

typeof(X, string)        :- string(X).

typeof(X, blob(T))       :- blob(X,T).

typeof(X, compound(F,A)) :- compound(X), X =.. [F|Args], length(Args,A).

typeof(X, ground)        :- ground(X).

typeof(X, cyclic)        :- cyclic_term(X).

typeof(X, list(Len))     :- is_list(X),length(X,Len).    % being a "list" is specific nonlocal phenomenon

typeof(X, conj(Len))     :- nonvar(X),is_conjunction(X,Len).
typeof(X, disj(Len))     :- nonvar(X),is_disjunction(X,Len).

typeof((_A->_B) , impl).
typeof(:-_A     , directive).
typeof(_H:-_B   , clause).

is_conjunction((_,B),Len) :- is_conjunction(B,Lb),!,Len is Lb+1.
is_conjunction((_,_),1).

is_disjunction((_;B),Len) :- is_disjunction(B,Lb),!,Len is Lb+1.
is_disjunction((_;_),1).

%
% TEST CODE
%

:- begin_tests(type_inspection).

test('empty list')      :- is_this_right([]               , [atomic,ground,nonvar,blob(reserved_symbol),list(0)]).
test('an atom')         :- is_this_right(foo_the_atom     , [atom,atomic,ground,nonvar,blob(text)]).
test('a string')        :- is_this_right("foo_the_string" , [atomic,ground,nonvar,string]).
test('a variable')      :- is_this_right(_VAR             , [var]).
test('integer')         :- is_this_right(4377                 , [atomic,ground,int,nonvar,number,rational]).
test('big integer')     :- is_this_right(43759624750933342417 , [atomic,ground,int,nonvar,number,rational]).
test('decimal')         :- is_this_right(5600.556 , [atomic,float,ground,nonvar,number]).
test('float')           :- A is 2/6, is_this_right(A, [atomic,float,ground,nonvar,number]).
test('/ compound')      :- is_this_right(2/6, [ground,nonvar,compound('/',2)]).
test('float')           :- is_this_right(1.234    , [atomic,float,ground,nonvar,number]).
test('rational')        :- is_this_right(5r6      , [atomic,ground,nonvar,number,rational]).
test('rational')        :- R is 5 rdiv 6, is_this_right(R, [atomic,ground,nonvar,number,rational]).
test('cyclic compound') :- A = a(1,2,3,A), is_this_right(A, [cyclic,ground,nonvar,compound(a,4)]).

test('dict')            :- is_this_right(_{a:1,b:2}, [nonvar,compound(dict,5)]).
test('cyclic list')     :- A = [1,2,3|A], is_this_right(A, [cyclic,ground,nonvar,compound(['|'],2)]).
test('nonempty list')   :- is_this_right([1,2,3]          , [ground,nonvar,list(3),compound(['|'],2)]).

setify(A,B,As,Bs) :-      % A,B -> As,Bs
   list_to_ord_set(A,As), % A->As
   list_to_ord_set(B,Bs). % B->Bs

is_this_right(Value,Expected) :-
   setof(T,typeof(Value,T),Ts),
   verdict(Value,Expected,Ts).

verdict(Value,Expected,Got) :-   % V,E,G -> b?
   setify(Expected,Got,Eos,Gos),  
   (ord_seteq(Eos,Gos)
    -> format("For ~w we have: ~w\n",[Value,Gos])
    ;  (format("For ~w, expected ~w but got ~w\n",[Value,Eos,Gos]),fail)).

:- end_tests(type_inspection).


rt :- run_tests(type_inspection).

If we run goal rt, we get this (not as well formatted):

These tests pass:

For [] we have: [atomic,ground,nonvar,blob(reserved_symbol),list(0)]
For foo_the_atom we have: [atom,atomic,ground,nonvar,blob(text)]
For foo_the_string we have: [atomic,ground,nonvar,string]
For _15852 we have: [var]
For 4377 we have: [atomic,ground,int,nonvar,number,rational]
For 43759624750933342417 we have: [atomic,ground,int,nonvar,number,rational]
For 5600.556 we have: [atomic,float,ground,nonvar,number]
For 0.3333333333333333 we have: [atomic,float,ground,nonvar,number]
For 2/6 we have: [ground,nonvar,compound(/,2)]
For 1.234 we have: [atomic,float,ground,nonvar,number]
For 5r6 we have: [atomic,ground,nonvar,number,rational]
For 5r6 we have: [atomic,ground,nonvar,number,rational]
For @(S_1,[S_1=a(1,2,3,S_1)]) we have: [cyclic,ground,nonvar,compound(a,4)]

But there there is a problem with the compound term with the | functor. What is the notation that I have to use to make the test pass?

For _15714{a:1,b:2}, 
expected [nonvar,compound(dict,5)] 
but got [nonvar,compound(dict,5)]

For @(S_1,[S_1=[1,2,3|S_1]]), 
expected [cyclic,ground,nonvar,compound([|],2)] 
but got [cyclic,ground,nonvar,compound([|],2)]

For [1,2,3], expected [ground,nonvar,list(3),compound([|],2)]
but got [ground,nonvar,list(3),compound([|],2)]

Also, is there a way to distinguish “big integers” and “machine integers” (except by checking magnitude?)

Leave a Reply

Your email address will not be published. Required fields are marked *