Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"Generic programming" in Prolog

As far as I know, Prolog does not have any built-in mechanisms for generic programming. It's possible to simulate generics using unification, but this requires type-checking at runtime:

:- initialization(main).
:- set_prolog_flag(double_quotes, chars).

% this is a "generic" predicate, where A and B have the same type
add(A,B,C) :-
    generic_types([A:Type,B:Type]),
    (Type = number,
    C is A + B;Type=var,C = A+B).

main :-
    add(A,B,C),
    add(3,4,D),
    writeln(C),
    writeln(D).

generic_types([]).
generic_types([A:B|C]) :-
    member(B,[var,nonvar,float,rational,number,atom,atomic,compound,callable,ground,acyclic_term]),
    call(B,A),
    generic_types(C).
has_type(Type,A) :-
    call(Type,A).

Is it possible to write "generic" predicates without checking the type of each variable at runtime?

like image 313
Anderson Green Avatar asked Oct 15 '22 11:10

Anderson Green


1 Answers

Some forms of generic programming are available in Prolog via Logtalk, which extends Prolog and can be used with most Prolog systems.

Given that you want different definitions of the add/3 predicate to be used depending on the type of the first two arguments, we can start by defining a protocol declaring the predicate:

:- protocol(math_protocol).

    :- public(add/3).

:- end_protocol.

Based in your sample code, we can now define different implementations for the predicate:

:- object(number,
    implements(math_protocol)).

    add(A, B, C) :-
        C is A + B.

:- end_object.


:- object(var,
    implements(math_protocol)).

    add(A, B, C) :-
        C = A + B.

:- end_object.

We can modify the number and var objects to also perform the type-checking. for example:

:- object(number,
    implements(math_protocol)).

    add(A, B, C) :-
        number(A),
        number(B),
        C is A + B.

:- end_object.

In alternative, we can define an object parametric on the type that performs the type-checking and then delegates the actual operation on the type. For example:

:- object(math(_Type_),
    implements(math_protocol)).

    add(A, B, C) :-
        call(_Type_, A),
        call(_Type_, B),
        _Type_::add(A, B, C).

:- end_object.

A sample call in this case would be:

?- math(number)::add(2, 3, Sum).
Sum = 5
yes

But note that these alternatives will still be performing type-checking at runtime.

The parametric object can be modified to find the type of the arguments as in your sample code. But that's quite inefficient without a built-in Prolog predicate that allows querying a term type (no standard for it and also not available in general, however).

like image 125
Paulo Moura Avatar answered Nov 15 '22 11:11

Paulo Moura