Search code examples
prologoptional-parametersdefault-parameters

Writing a Prolog predicate to supply default parameters to another predicate


I'm learning Prolog and I followed this tutorial for graphs. Here's my code:

path(X, Y, Length, [X,Y], _) :- 
   connect(X, Y, Length).   
path(X, Y, Length, [X|P], V) :- 
   \+ member(X, V),
   connect(X, Z, Length1),
   path(Z, Y, Length2, P, [X|V]),
   Length is Length1 + Length2.

To use this, I call

?- path(a, f, Length, Path, []).

However, I would like to shorten it to:

?- path(a, f, Length, Path).

But I can't get the default parameters to work.


Solution

  • Quite straight-forward: just define a predicate with the same name that calls the predicate doing the work:

    path(X, Y, Length, Path) :-
        path(X, Y, Length, Path, []).
    

    In Prolog, predicates are always referred to with Name/Arity because same name but different arity makes them two separate predicates. So, now your program will have both path/4 and path/5 defined.

    As to naming: if both predicates are part of your interface, they should have the same name. Many examples in Prolog standard libraries, for example format/1, format/2, format/3.

    If, however, the working predicate is only meant to be used as a helper predicate, usually you give it a suffix. To use something simple as list_max/2:

    list_max([X|Xs], Max) :-
        list_max_SUFFIX(Xs, X, Max).
    

    I have seen code where SUFFIX is just an underscore: list_max_/3 (and you keep adding underscores for more helper predicates down the same predicate lineage); or, underscore + number: list_max_1/3 (and you increment the number); or, _aux + optional number, if you have more: list_max_aux/3. Using the underscore + number:

    list_max_1([], Max, Max).
    list_max_1([X|Xs], Max0, Max) :-
        compare(Order, X, Max0),
        list_max_2(Order, X, Max0, Xs, Max).
    
    list_max_2(<, _, Max0, Xs, Max) :- list_max_1(Xs, Max0, Max).
    list_max_2(=, _, Max0, Xs, Max) :- list_max_1(Xs, Max0, Max).
    list_max_2(>, X, _, Xs, Max) :- list_max_1(Xs, X, Max).
    

    But wait, there's more. If you use a naming scheme for your predicates where the name represents the arguments, you get for example setup_call_cleanup/3, and call_cleanup/2, defined as setup_call_cleanup(true, Goal, Cleanup). With this naming scheme, you would call your "path" predicates maybe from_to_length_path/4 and from_to_length_path_acc/5. I find this naming scheme nice because it is self-documenting, but as this example shows, it could become excessive if your predicate has too many arguments.