Coding Guidelines for Prolog (2011)
Very interesting! Let&#x27;s consider for example the definition of sum_list&#x2F;2 which is shown in Fig. 1, Fig. 2 and Fig. 3:<p><pre><code>    %% sum_list(+Number_List, ?Result)
    % Unifies Result with the sum of the numbers in Number_List;
    % calls error&#x2F;1 if Number_List is not a list of numbers.
    sum_list(Number_List, Result) :-
            sum_list(Number_List, 0, Result).
    
    % sum_list(+Number_List, +Accumulator, ?Result)
    sum_list([], A, A). % At end: unify with accumulator.
    sum_list([H|T], A, R) :- % Accumulate first and recur.
            number(H),
            !,
            B is A + H,
            sum_list(Rest, B, R).
    sum_list(_, _A, _R) :- % Catch ill-formed arguments.
            error(&#x27;first arg to sum_list&#x2F;2 not a list of numbers&#x27;).
</code></pre>
Compiling it with Scryer Prolog, I get a warning:<p><pre><code>    $ scryer_prolog sum_list.pl
    Warning: singleton variables T, Rest at line 4 of sum_list.pl
       true.
</code></pre>
A singleton variable often indicates a mistake in the code. And indeed, the sample code uses Rest where it apparently meant to use T (or vice versa). So, I change the second clause of sum_list&#x2F;3 to:<p><pre><code>    sum_list([H|T], A, R) :-
            number(H),
            !,
            B is A + H,
            sum_list(T, B, R).
</code></pre>
And now we&#x27;re ready to use the predicate! Let&#x27;s ask Prolog the most general query: <i>Which answers are there in general?</i> So, a query where all arguments are logic variables:<p><pre><code>    ?- sum_list(Ls, R).
       Ls = [], R = 0
    ;     error(existence_error(procedure,error&#x2F;1),error&#x2F;1).
</code></pre>
The existence error is due to the use of the non-standard predicate error&#x2F;1 in the code sample. The predicate apparently meant to throw an exception telling us:<p><pre><code>    first arg to sum_list&#x2F;2 not a list of numbers
</code></pre>
But <i>the query did not restrict the first argument at all</i>, so it may just as well be a list of numbers! The predicate probably meant to say that the argument is <i>not sufficiently instantiated</i>. In that case, it should have thrown an <i>instantiation error</i>. The standard predicate (is)&#x2F;2 throws such an instantiation error for us in such cases. Also, it throws <i>type errors</i> for us! A type error is categorically different from an instantiation error: From a logical perspective, a type error can be replaced by silent failure, but an instantiation error can not.<p>We can therefore write the second clause as:<p><pre><code>    sum_list([H|T], A, R) :-
            B is A + H,
            sum_list(T, B, R).
</code></pre>
and also remove the third clause entirely. We now get:<p><pre><code>    ?- sum_list(Ls, R).
       Ls = [], R = 0
    ;     error(instantiation_error,(is)&#x2F;2).
</code></pre>
From a logical perspective, that&#x27;s OK: The predicate tells us that too little is known to make any statement, and a more specific query may yield solutions.<p>Also, this version correctly distinguishes between type and instantiation errors, and we now get for example:<p><pre><code>    ?- sum_list(&quot;abc&quot;, R).
       error(type_error(evaluable,a),(is)&#x2F;2).
</code></pre>
As I see it, a key attraction of logic programming is that we are able to reason logically about our code. This holds as long as certain logical properties are preserved. The paper hints at such properties for example with the concept of <i>steadfastness</i>, which it defines in Section 5.1: A predicate &quot;<i>must work correctly if its output variable already happens to be instantiated to the output value</i>&quot;. How can we tell though which variables are output variables, and also why even distinguish a particular variable as &quot;output variable&quot;? Should this not hold for <i>all</i> variables?<p>A particularly important logical property is called <i>monotonicity</i>: Generalizing a query (or program) can at most add solutions, never remove them. With monotonic predicates, debugging is very nice: For instance, if a predicate unexpectedly fails, then we can <i>generalize</i> it by removing goals, and if the remaining fragment <i>still</i> fails unexpectedly, then there must be a mistake <i>in that fragment</i>. Scryer Prolog provides library(debug) for this approach of <i>declarative debugging</i>:<p><a href="https:&#x2F;&#x2F;www.scryer.pl&#x2F;debug" rel="nofollow noreferrer">https:&#x2F;&#x2F;www.scryer.pl&#x2F;debug</a><p>In Scryer Prolog, we can define the specific case of arithmetic over integers with <i>constraints</i> over integers, for example like this:<p><pre><code>    :- use_module(library(clpz)).
    :- use_module(library(lists)).
    :- use_module(library(lambda)).
    
    list_sum(Is, S) :- foldl(\I^S0^S^(S #= S0 + I), Is, 0, S).
</code></pre>
Higher-order predicates such as maplist&#x2F;N and foldl&#x2F;N retain logical properties of the predicates that occur as arguments.<p>The most general query now works as expected:<p><pre><code>    ?- list_sum(Is, Sum).
       Is = [], Sum = 0
    ;  Is = [Sum], clpz:(Sum in inf..sup)
    ;  Is = [_A,_B], clpz:(_A+_B#=Sum)
    ;  Is = [_A,_B,_D], clpz:(_A+_B#=_C), clpz:(_C+_D#=Sum)
    ;  ... .
</code></pre>
The predicate does not terminate, <i>as expected</i>, because we expect solutions for lists of <i>all lengths</i>:<p><pre><code>    ?- list_sum(Is, Sum), false.
    loops.
</code></pre>
And other cases are simply specific <i>instances</i> of the most general query:<p><pre><code>    ?- list_sum([1,2,3], Sum).
       Sum = 6.
</code></pre>
Note that I have changed the predicate name from sum_list&#x2F;2 to list_sum&#x2F;2, because the list is the <i>first</i> argument, and the sum is the <i>second</i> argument. So, I am now using &quot;sum&quot; no longer as a <i>verb</i>, but as a <i>noun</i>, because that seems more appropriate for code that is declarative, not imperative: We describe what is true, not what must be done, and our code works in all directions and also with different execution strategies. integers_sum&#x2F;2 may be an even better name in this case.<p>One other naming convention I like to use is to append an &quot;s&quot; for logic variables that stand for lists, such as &quot;Is&quot; for a list of integers.