26

I have come across an unfamiliar bit of Prolog syntax in Lee Naish's paper Higher-order logic programming in Prolog. Here is the first code sample from the paper:

% insertion sort (simple version)
isort([], []).
isort(A.As, Bs) :-
    isort(As, Bs1),
    isort(A, Bs1, Bs).

% insert number into sorted list
insert(N, [], [N]).
insert(N, H.L, N.H.L) :-
    N =< H.
insert(N, H.LO, H.L) :-
    N > H,
    insert(N, LO, L).

My confusion is with A.As in isort(A.As, Bs) :-. From the context, it appears to be an alternate cons syntax for lists, the equivalent of isort([A|As], Bs) :-.

As well N.H.L appears to be a more convenient way to say [N|[H|L]].

But SWI Prolog won't accept this unusual syntax (unless I'm doing something wrong).

Does anyone recognize it? is my hypothesis correct? Which Prolog interpreter accepts that as valid syntax?

false
  • 10,264
  • 13
  • 101
  • 209
Nick Knowlson
  • 7,185
  • 6
  • 47
  • 63

3 Answers3

33

The dot operator was used for lists in the very first Prolog system of 1972, written in Algol-W, sometimes called Prolog 0. It is inspired by similar notation in LISP systems. The following exemple is from the paper The birth of Prolog by Alain Colmerauer and Philippe Roussel – the very creators of Prolog.

+ELEMENT(*X, *X.*Y).
+ELEMENT(*X, *Y.*Z) -ELEMENT(*X, *Z).

At that time, [] used to be NIL. All remaining the same in the next version Prolog I written in Fortran by Battani & Meloni.

Then DECsystem 10 Prolog used cases to distinguish atoms and variables and introduced the square bracket notation replacing nil and X.Xs with [] and [X,..Xs] which in later versions of DECsystem 10 received [X|Xs] as an alternative. In ISO Prolog, there is only [X|Xs], .(X,Xs), and as canonical syntax '.'(X,Xs).

Please note that the dot has many different rôles in ISO Prolog. It serves already as

  • end token when followed by a % or a layout character like SPACE, NEWLINE, TAB.

  • decimal point in a floating point number, like 3.14159

  • graphic token char forming graphic tokens as =..

So if you are now declaring . as an infix operator, you have to be very careful. Both with what you write and what Prolog systems will read. A single additional space can change the meaning of a term. Consider two lists of numbers in both notations:

[1,2.3,4]. [5].
1 .2.3.4.[]. 5.[].

Please note that you have to add a space after 1. In this context, an additional white space in front of a number may change the meaning of your terms. Like so:

[1|2.3]. [4]. 5. [].
1 .2.3. 4.[]. 5. [].

Here is another example which might be even more convincing:

[1,-2].
1.(-2).[].

Negative numbers require round brackets within dot-lists.

Today, there is only YAP and XSB left that still offer infix . by default – and they do it differently. And XSB does not even recognize above dot syntax: you need round brackets around some of the nonnegative numbers.

You wrote that N.H.L appears to be a more convenient way to say [N|[H|L]]. There is a simple rule-of-thumb to simplify such expressions in ISO Prolog: Whenever you see within a list the tokens | and [ immediately after each other, you can replace them by , (and remove the corresponding ] on the right side). So you can now write: [N,H|L] which does not look that bad.

You can use that rule also in the other direction. If we have a list [1,2,3,4,5] we can use | as a "razor blade" like so: [1,2,3|[4,5]].


Another remark, since you are reading Naish's paper: In the meantime, it is well understood that only call/N is needed! And ISO Prolog supports call/1, call/2 up to call/8.

false
  • 10,264
  • 13
  • 101
  • 209
  • 4
    Wow, what a thorough answer, thanks so much! `[N,H|L]` is indeed much nicer, I had no idea that was valid. I also appreciate the pointer to that exchange, that's really interesting. – Nick Knowlson Apr 05 '12 at 14:57
  • 4
    You might be interested to see [how lambdas fit with `call/N`](http://www.complang.tuwien.ac.at/ulrich/Prolog-inedit/ISO-Hiord). – false Apr 06 '12 at 09:24
  • 3
    i just checked and the dotted pair appears in mccarthy's first paper on lisp - http://www.cs.unm.edu/~luger/cs451/resources/recursive.pdf (section 3) – andrew cooke Apr 16 '12 at 00:02
  • 1
    @andrew cooke: Not only the dot, but also the comma is used in the paper - there is a revealing footnote#3 on page 9. – false Apr 16 '12 at 00:17
  • 1
    @false are you aware of 10 edits rule? you risk this answer turning into community wiki, so no more future rep to you and no rep at all to your tag total too. Just so you know. :) – Will Ness Sep 23 '12 at 20:09
  • 1
    @Will Ness: Thank you. No, I am unaware of that rule (I always hope for finding a good SO lawyer...). It is a bit of a pity: There should still be some more details in the response to understand the precise syntax of `-1`. – false Sep 23 '12 at 20:50
  • 2
    ah, good that I told you then. :) You have two options (but I'm not a lawyer): add another answer or after making another edit flag your answer and ask moderator to de-community-wiki it. But that's an extreme recourse (I did it once, for same reasons). Or you can ask about this on meta. :) – Will Ness Sep 24 '12 at 09:42
9

Yes, you are right, the dot it's the list cons infix operator. It's actually required by ISO Prolog standard, but usually hidden. I found (and used) that syntax some time ago:

:- module(eog, []).
:- op(103, xfy, (.)).

% where $ARGS appears as argument, replace the call ($ARGS) with a VAR
% the calle goes before caller, binding the VAR (added as last ARG)
funcs(X, (V, Y)) :-
    nonvar(X),
    X =.. W.As,

    % identify meta arguments
    (   predicate_property(X, meta_predicate M)
        % explicitly exclude to handle test(dcg)
        % I'd like to handle this case in general way...
    ,   M \= phrase(2, ?, ?)
    ->  M =.. W.Ms
    ;   true
    ),

    seek_call(As, Ms, Bs, V),
    Y =.. W.Bs.

% look for first $ usage
seek_call([], [], _Bs, _V) :-
    !, fail.
seek_call(A.As, M.Ms, A.Bs, V) :-
    M @>= 0, M @=< 9, % skip meta arguments
    !, seek_call(As, Ms, Bs, V).
seek_call(A.As, _, B.As, V) :-
    nonvar(A),
    A = $(F),
    F =.. Fp.FAs,
    (   current_arithmetic_function(F) % inline arith
    ->  V = (PH is F)
    ;   append(FAs, [PH], FBs),
        V =.. Fp.FBs
    ),
    !, B = PH.
seek_call(A.As, _.Ms, B.As, V) :-
    nonvar(A),
    A =.. F.FAs,
    seek_call(FAs, Ms, FBs, V),
    !, B =.. F.FBs.
seek_call(A.As, _.Ms, A.Bs, V) :-
    !, seek_call(As, Ms, Bs, V).

:- multifile user:goal_expansion/2.
user:goal_expansion(X, Y) :-
    ( X = (_ , _) ; X = (_ ; _) ; X = (_ -> _) )
    -> !, fail % leave control flow unchanged (useless after the meta... handling?)
    ;  funcs(X, Y).

/* end eog.pl */

I was advised against it. Effectively, the [A|B] syntax it's an evolution of the . operator, introduced for readability.

OT: what's that code?

the code above it's my attempt to sweeten Prolog with functions. Namely, introduces on request, by means of $, the temporary variables required (for instance) by arithmetic expressions

fact(N, F) :-
     N > 1 -> F is N * $fact($(N - 1)) ; F is 1.

each $ introduce a variable. After expansion, we have a more traditional fact/2

?- listing(fact).
plunit_eog:fact(A, C) :-
    (   A>1
    ->  B is A+ -1,
        fact(B, D),
        C is A*D
    ;   C is 1
    ).

Where we have many expressions, that could be useful...

CapelliC
  • 59,646
  • 5
  • 47
  • 90
  • Thanks very much for the answer! I don't understand how `$` is working, but it is very compelling in the example with `$fact` - I will have to spend some time getting to know this code. – Nick Knowlson Apr 06 '12 at 03:17
  • To test that code your Prolog must provide goal_expansion/2 and modules. Both are de-facto extensions, not ISO approved. Sorry for that. In effect, ISO Prolog it's a very restricted language WRT available implementations, it's one of the major Prolog problems... – CapelliC Apr 06 '12 at 06:55
  • 2
    @chac: systems still do change their meaning of `goal_expansion/2` and the like on a release-to-release basis. That is practically impossible to standardize. – false Apr 16 '12 at 00:23
  • @false: it's not really easy to use, either. SWI-Prolog locks easily while recompiling tentative code, or better, generate bulks of redefinition messages and goes in sort of recompile loop (I think). Fortunately a recent post by Jan on SWI-Prolog list clarified the question. – CapelliC Apr 16 '12 at 01:13
  • @chac: The meaning is still different between SWI and SICStus. It would be great already, if systems would conform to the **existing** standard. [There is progress](http://www.complang.tuwien.ac.at/ulrich/iso-prolog/conformity_assessment#progress), but there could be much more. – false Apr 16 '12 at 01:28
8

This syntax comes from NU-Prolog. See here. It's probably just the normal list functor '.'/2 redefined as an infix operator, without the need for a trailing empty list:

?- L= .(a,.(b,[])).
L = [a,b]
Yes (0.00s cpu)
?- op(500, xfy, '.').
Yes (0.00s cpu)
?- L = a.b.[].
L = [a,b]
Yes (0.00s cpu)
false
  • 10,264
  • 13
  • 101
  • 209
twinterer
  • 2,416
  • 1
  • 15
  • 13
  • 4
    The syntax is much older that NU (1987) or MU. – false Apr 05 '12 at 11:34
  • @false: makes me curious. Can you point to any references that are easily available? I've never seen it used as infix operator anywhere else. Can't remember having seen it in C&M, for instance. – twinterer Apr 05 '12 at 11:44
  • I added most salient reference to my answer. – false Apr 05 '12 at 13:17