5

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?

Erik Kaplun
  • 37,128
  • 15
  • 99
  • 111
Anderson Green
  • 30,230
  • 67
  • 195
  • 328
  • 1
    Prolog is *dynamically* typed. So there is not much type checking, just like in Python. – Willem Van Onsem Sep 21 '19 at 18:16
  • 1
    This is a bit like asking how one would simulate a clutch in an automatic transmission car. "Generic programming" per se is only applicable to languages with static type checking. Every Prolog predicate is already generic in terms of the types it accepts, and there is no other kind of type checking in Prolog besides runtime. It's the same situation in Python and every other dynamically typed language. Python's "type system" has no ramifications on the _semantics_ of Python programs or their evaluation, by design. – Daniel Lyons Sep 21 '19 at 20:27
  • A minor remark: The types that make sense in your case **do not** include `var`. See [this](https://stackoverflow.com/q/24017420/772868) for more. – false Sep 22 '19 at 18:06

2 Answers2

2

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

Paulo Moura
  • 18,373
  • 3
  • 23
  • 33
1

You could emulate parametric polymorphism by explicitly adding the type parameter to the predicate:

add(int, A, B, C) :-
    C is A + B.
add(var, A, B, C) :-
    C #= A + B.


?- add(var, 2, 3, 5).
true.

?- add(var, A, 3, 5).
A = 2.

?- add(int, A, 3, 5).
ERROR: Arguments are not sufficiently instantiated
Erik Kaplun
  • 37,128
  • 15
  • 99
  • 111
  • 1
    Instead of passing the type as a parameter, you could also use Prolog's [type-checking predicates](https://stackoverflow.com/a/3866440/975097). – Anderson Green May 02 '21 at 14:43