From: ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe)
Newsgroups: comp.lang.prolog
Subject: Re: Writing portable code.
Date: 6 Oct 92 04:31:39 GMT
Organization: Comp Sci, RMIT, Melbourne, Australia

In article <ALBERTO.92Sep29141859@karna.cs.umd.edu>, alberto@cs.umd.edu (Jose Alberto Fernandez R) writes:
> In article <14758@goanna.cs.rmit.oz.au> ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) writes:
>    Nice if conditional compilation had been provided as part of Prolog?
>    But it doesn't need to be.

> 1) Its granularity -- The minimum level of control that you have is a
> clause. There is no way to specify that some part of a clause must
> change depending on the installation. In particular, the way you offer
> to solve the original problem is something like:
> 
> :- if(environment(dialect(quintus))).
> retract_all_clauses(X) :- retractall(X).
> :- endif.
> :- if (environment(dialect(sepia))).
> retract_all_clauses(X) :- retract_all(X).
> :- endif.
> 
> which has the overhead of an extra call each time you want to retract
> all clauses.

And what is that overhead?  ONE JUMP INSTRUCTION!  Big deal.

> In my approach, as I said, one can define a macro RETRACTALL(x) that
> will expand to the correct prolog term depending on the installation.
> Although I think this problem could be solved by adding to your preprocesor
> a facility to define rewriting rules (simple rewriting rules).

I find it incredibly ugly to use a name like RETRACTALL which violates
the normal Prolog convention that capital letters begin variables.  The
C preprocessor is perfectly happy with lower case macro names.   If you
want a portable preprocessor for langauges with lexical structure not
utterly unlike C, M4 is pretty good (and there's a free version).  That
too is happy with macro names that don't look like variables.

And yes, of course a separate preprocessor can provide macros in Prolog.
The Craft of Prolog describes how to do that using term_expansion/2, and
it isn't any harder in a separate preprocessor.

I wrote:
>    There are several problems with using the C pre-processor to do this job.
>    The first is that the C pre-processor is a *C* pre-processor, and it uses
>    C syntax, not Prolog syntax.  For example, it does not understand "%"
>    comments.  It may well report syntax errors for things that are valid
>    Prolog:  'FLIGHT' is not portable C.  It may get things wrong:
> 	   0x
>    is perfectly valid Prolog, in the presence of a postfix operator
>    declaration for x, but it isn't valid C.  It may generate output that is
>    not valid Prolog (but you can read the ANSI C standard for yourself).
The reply is

> I just try your examples and it works just fine. Of cource you must
> use the -C and -P flags to tell the processor not generate 'C'
> specific output.

And this reveals a deep misunderstanding of the problem.  First, THERE MAY
NOT BE ANY C PREPROCESSOR.  Second, even if there is, the ANSI standard does
not specify any flags for it.  Third, -C and -P DO ***NOT*** tell the
UNIX C preprocessors "not to generate 'C' specific output."
According to the manual for the C compiler I use:
	-C means to preserve /**/ comments instead of deleting them
	-P means to push all the *.c files named in the command line
	   through the preprocessor, generating corresponding *.i files.
There is, for example, no option in this compiler to suppress the generation
of "# line" directives in the output.  GCC interprets -P as "don't generate
"# line" directives, but not every UNIX C compiler is GCC.  I repeat, the
vendor-supplied PCC-derived C compiler I normally use does not have a
documented option to do that.

But fourth, even if all C preprocessors did have -C and -P in the required
sense, that misses the point.  My point was that CPP requires C ***input***.
Here is an example:

	Line 1:	"p(X, Y) :- Y is X/\"
	Line 2: "1."

This is legal well defined Prolog, meaning Y is X /\ 1.  But an ANSI
C preprocessor is required to discard "\<newline>".  Let's see what
GCC does to this example:

	% cat >foo.c
	p(X,Y):-Y is X/\
	1.
	% gcc -ansi -pedantic -C -P -E foo.c
	p(X,Y):-Y is X/1.

Do you see what happened?  The backslash quietly disappeared.  This is not
the kind of behaviour we want in a preprocessor for Prolog code!
Then there are fun things that happen due to the definition of 
"preprocessing tokens".  For example, 3e+xy is a valid preprocessing token.
Now, let's see...
	:- op(10, xf, e).	% so 3e stands for e(3)
	#define xy 4e
	example(3e+xy).		% want example(e(3) + e(4)).
GCC's output is
	:- op(10, xf, e).       % so 3e stands for e(3)

	example(3e+xy).
in which the intended replacement of xy by 4e has _not_ occurred.
This is not a bug; GCC is right.  That's what a C preprocessor must do.
But it is not what we want a Prolog macro processor to do!

>    The second major problem with using the C pre-processor is that it may not
>    exist.  An ANSI C compiler may build the pre-processor in (and some do).
>    I've used a Classic C compiler which did not have a separate preprocessor
>    and gave you no access to the preprocessed output.
> 
> There are other systems that use CPP. For example 'xrdb' the resource
> database loader for X11 uses CPP to macro expand resource files.

So what?  Just because the X11 people do non-portable things, does that
mean they thereby become portable?  I repeat,
	Many of Quintus's customers didn't have _any_ C compiler.
	An ANSI conforming C compiler does not have to provide preprocessing
	as a separate step.
	Some ANSI conforming C compilers _don't_.
The advantage of using a conditional processor (and macro expander) written
in Prolog is that if you have Prolog, you have what you need.

> I
> think that the usage of a macro processor DIFFERENT from the object
> language itself is a very good way to separate the codification of two
> very different problems a) how to express my algorithm. b) How to
> install for a particular system.

I do _not_ think that it is a good idea to use a macro processor which
does not know how to tokenise the language you are trying to preprocess.
I have given two examples where you get surprising results.  I can give
many more.

> As I said in my previous message, the problem with this is that you
> are mixing your declarative program, with the declaratios of how the
> code must be process in a particular installation. I don't like that,

Well, _I_ like the fact that the information is expressed once in one
way.  I got sick of maintaining environment information as
	shell scripts +
	C headers +
	Prolog macros +
	Emacs Lisp files +
	assembler macros
years ago.  I am firmly convinced that environment information should be
_maintained_ in one place.  (And if C headers are wanted, write a Prolog
program to generate them from the Prolog table.)

> I believe that the programmer should be able to clearly distinguish
> between the two. That facilitates the portability of software to
> different architectures.

I think you have misunderstood the real intent of the environment/1 table.
It gives us a portable way to write adaptable code WITHOUT conditonal
processing; all we need is a little bit of code inlining and optimisation.

For example,

	retract_all_clauses(Head) :-
		environment(dialect(quintus)),
		retractall(Head).
	retract_all_clauses(Head) :-
		environment(dialect(sepia)),
		retract_all(Head).

Now, a Prolog compiler can very easily unfold these calls to environment/1
at compile time, getting, say,

	retract_all_clauses(Head) :-
		true,
		retractall(Head).
	retract_all_clauses(Head) :-
		fail,
		retract_all(Head).

It is the very simplest of optimisations to throw the second clause away,
and any WAM-based compiler will yield something like
	retract_all_clauses/1:
		goto retractall/1
as the translation.

It is precisely because we use the SAME language for describing the
environment as the language we are conditionalising that this works.
The code is correct even _without_ any sort of compile time conditionalisation.
It can be compiled efficienctly just using inlining, which we want anyway.
(Oh yes, inlining can be done by a separate program; it doesn't have to be
the compiler.  That's what ProMix and Mixtus are, in a way, except more so.)

-- 
RMIT policy requires the use of Gender Inclusive Language in all publications
and in all information conveyed to students, staff, and potential job
applicants.  Lapses into English are the sole responsibility of the author.
The Constitution of Australia does _not_ guarantee Freedom of Speech.