Search code examples
typesprologswi-prolog-for-sharing

Defining a type hierarchy including function types in Prolog


I'm revisiting Prolog after studying it in college, and would like to describe a type hierarchy that includes function types. So far, this is what I got (SWISH link):

% subtype/2 is true if the first argument is a direct subtype of
% the second.
subtype(bit, byte).
subtype(byte, uint16).
subtype(uint16, uint32).
subtype(uint32, uint64).
subtype(uint64, int).

subtype(int8, int16).
subtype(int16, int32).
subtype(int32, int64).
subtype(int64, int).

% isa/2 checks if there's a sequence of types that takes
% from X to Y.
isa(X,Y) :- subtype(X,Y).
isa(X,Y) :-
    subtype(X,Z),
    isa(Z,Y).

This program works well for the following queries:

?- subtype(bit, int).
true
?- findall(X,isa(X,int),IntTypes).
IntTypes = [uint64, int64, bit, byte, uint16, uint32, int8, int16, int32]

I then added the following definition for function subtypes just above isa, where a function is a complex term func(ArgsTypeList, ResultType):

% Functions are covariant on the return type, and
% contravariant on the arguments' type.
subtype(func(Args,R1), func(Args,R2)) :-
    subtype(R1, R2).
subtype(func([H1|T],R), func([H2|T],R)) :-
    subtype(H2, H1).
subtype(func([H|T1],R), func([H|T2],R)) :-
    subtype(func(T1,R), func(T2,R)).

Now, I am still able to do some finite checks, but even trying to enumerate all subtypes of byte fails with stack overflow.

?- isa(func([int,int], bit), func([bit,bit], int)).
true
?- isa(X, byte).
X = bit ;
Stack limit (0.2Gb) exceeded

What am I doing wrong?


Solution

  • I was able to avoid the issue of unbounded left-recursion by including the logic for supertypes, and using one or another depending on which of the variables are bound.

    First, I defined a clause for simple types, enumerating all that are used later explicitly:

    simple_type(bit).
    simple_type(byte).
    % ...
    simple_type(int).
    

    I then restricted the subtype rule only for cases where the first term is already bound, using nonvar.

    subtype(func(Args,R1), func(Args,R2)) :-
        nonvar(R1),
        subtype(R1, R2).
    subtype(func([H1|T],R), func([H2|T],R)) :-
        nonvar(H1),
        supertype(H1, H2).
    subtype(func([H|T1],R), func([H|T2],R)) :-
        nonvar(T1),
        subtype(func(T1,R), func(T2,R)).
    

    I then defined a supertype rule, that is just the opposite of subtype for simple types...

    supertype(X, Y) :-
        simple_type(X),
        subtype(Y, X).
    

    ...but is fully duplicated for function types.

    supertype(func(Args,R1), func(Args,R2)) :-
        nonvar(R1),
        supertype(R1, R2).
    supertype(func([H1|T],R), func([H2|T],R)) :-
        nonvar(H1),
        subtype(H1, H2).
    supertype(func([H|T1],R), func([H|T2],R)) :-
        nonvar(T1),
        supertype(func(T1,R), func(T2,R)).
    

    isa is still the same, with two additions:

    • A type is the same as itself (i.e., an int is-a int).
    • If the first term is not bound but the second is, use the inverse rule typeof.

    _

    isa(X,Y) :- X = Y.
    isa(X,Y) :- subtype(X,Y).
    isa(X,Y) :-
        nonvar(X),
        subtype(X,Z),
        isa(Z,Y).
    isa(X,Y) :-
        var(X), nonvar(Y), typeof(Y,X).
    

    Finally, typeof is just the opposite of isa, using supertype instead of subtype:

    typeof(X,Y) :- X = Y.
    typeof(X,Y) :- supertype(X,Y).
    typeof(X,Y) :-
        nonvar(X),
        supertype(X,Z),
        typeof(Z,Y).
    typeof(X,Y) :-
        var(X), nonvar(Y), isa(Y,X).
    

    I noticed that there are a lot of inefficiencies and duplicate results with these rules, but at least it's working :)

    ?- setof(X, isa(func([byte, byte], uint32), X), All), length(All, L).
    
    All = [func([bit, bit], int), func([bit, bit], uint32), func([bit, bit], uint64), func([bit, byte], int), func([bit, byte], uint32), func([bit, byte], uint64), func([byte, bit], int), func([byte, bit], uint32), func([byte, bit], uint64), func([byte, byte], int), func([byte, byte], uint32), func([byte, byte], uint64)],
    L = 12