🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Thread View: comp.lang.prolog
9 messages
9 total messages Started by Kjell Post Sun, 27 Sep 1992 08:54
Writing portable code.
#1493
Author: Kjell Post
Date: Sun, 27 Sep 1992 08:54
18 lines
1094 bytes
Let me explain by an example what I'm looking for.  I want to write a
program that works for both SICStus Prolog and Sepia.  The problem is
that I need to retract all clauses foo/n when the program is started.
Now, in SICStus the procedure is called retractall/2, but in Sepia it
is retract_all/2.  That extra underscore (or absence of it) is really 
annoying!  So I'd like to hear about efficient and portable tricks to
write code that works in both languages.

	Should I use call/1?  

	Should I write "retractall(H) :- retract_all(H)." and rely
	on the fact that SICStus just issues a warning when an
	a built-in predicate is being redefined?

	Any better ideas?

--Kjell	kjell@cs.ucsc.edu
Re: Writing portable code.
#1494
Author: Paul Singleton
Date: Sun, 27 Sep 1992 14:00
33 lines
1954 bytes
From article <1a3srjINNsgm@darkstar.UCSC.EDU>, by kjell@cse.ucsc.edu (Kjell Post):
 
> Let me explain by an example what I'm looking for.  I want to write a
> program that works for both SICStus Prolog and Sepia.  The problem is
> that I need to retract all clauses foo/n when the program is started.
> Now, in SICStus the procedure is called retractall/2, but in Sepia it
> is retract_all/2.  That extra underscore (or absence of it) is really 
> annoying!  So I'd like to hear about efficient and portable tricks to
> write code that works in both languages.

    invent some novel name for the facility you require
    (e.g. retract_all_matching_unit_clauses)

    write your portable code in terms of this

    write compatibility libraries for each target Prolog implementation
    e.g.
	retract_all_matching_unit_clauses( X) :-
		retractall( X).

    to port to Prolog X:

	link your source with the Prolog X compatibility library (linking
	in Prolog is particularly trivial: 'cat' does it)

	feed the result through a general-purpose partial evaluator
	(e.g. Dan Sahlin's Mixtus) to eliminate the redundant procedures.

----
  __   __    Paul Singleton (Mr)           JANET: paul@uk.ac.keele.cs
 |__) (__    Computer Science Dept.        other: paul@cs.keele.ac.uk
 |  .  __).  Keele University, Newcastle,    tel: +44 (0)782 621111 x7355
             Staffs ST5 5BG, ENGLAND         fax: +44 (0)782 713082
Re: Writing portable code.
#1500
Author: Jose Alberto Fernandez R
Date: Mon, 28 Sep 1992 15:35
50 lines
2285 bytes
In article <1a3srjINNsgm@darkstar.UCSC.EDU> kjell@cse.ucsc.edu (Kjell Post) writes:

   Let me explain by an example what I'm looking for.  I want to write a
   program that works for both SICStus Prolog and Sepia.  The problem is
   that I need to retract all clauses foo/n when the program is started.
   Now, in SICStus the procedure is called retractall/2, but in Sepia it
   is retract_all/2.  That extra underscore (or absence of it) is really 
   annoying!  So I'd like to hear about efficient and portable tricks to
   write code that works in both languages.

One way to solve this is to use in the code macro commands that
expand to the right thing for each system. Assume that your
distributed source code is in files with extensions `.pli'. 
In your case, the macro definitions look something like this:

	#ifdef SEPIA
	#define RETRACTALL(x) retract_all(x)
	#else
	#define RETRACTALL(x) retractall(x)
	#endif

and in your code you use the macro RETRACTALL(x):

	...:- ..., RETRACTALL(foo(X,Y)), ....

To generate the right code, provide a makefile which contains
the following:

	PROLOG = QUINTUS
	    :
	all: list of source files with .pl extension.
	    :
	    :
	.pli.pl: 
		/lib/cpp -C -P -D$(PROLOG) $*.pli $*.pl

So if you want to generate code for SEPIA you just need to change one
line in the makefile or just do:

	make PROLOG=SEPIA

It would be nice if a facility like this would have been provided as
part of the language. Then we wouldn't need to borrow tools from 'C'.

	Jose Alberto.
--
:/       \ Jose Alberto Fernandez R | INTERNET: alberto@cs.umd.edu
:| o   o | Dept. of Computer Sc.    | BITNET: alberto@cs.umd.edu
:|   ^   | University of Maryland   | UUCP: {...}!mimsy!alberto
:\  \_/  / College Park, MD 20742   | 
Re: Writing portable code.
#1506
Author: Richard A. O'Keefe
Date: Tue, 29 Sep 1992 03:50
129 lines
4934 bytes
In article <ALBERTO.92Sep28113506@karna.cs.umd.edu>, alberto@cs.umd.edu (Jose Alberto Fernandez R) writes:
> It would be nice if a facility like this would have been provided as
> part of the language. Then we wouldn't need to borrow tools from 'C'.

Nice if conditional compilation had been provided as part of Prolog?
But it doesn't need to be.

Suppose we use
    :- if(Condition).
    /* stuff */
    :- endif.
for conditional processing, and let them nest.

conditional_copy(InputFile, OutputFile) :-
	seeing(OldInput),
	telling(OldOutput),
	see(InputFile),
	tell(OutputFile),
	conditional_copy_loop(0, 0).
	told,
	tell(OldOutput),
	seen,
	see(OldInput).

conditional_copy_loop(Trues, Falses) :-
    /*  We are inside Falses false conditionals which
	are inside Trues true conditionals
    */
	read(Term),
	expand_term(Term, Expansion),
	conditional_copy_loop(Expansion, Trues, Falses).

conditional_copy_loop(end_of_file, T, F) :- !,
	StillOpen is T + F,
	(   StillOpen > 0 ->
	    format(user_error, '~N! ~w :- endifs missing.~n', [StillOpen])
	;   true
	).
conditional_copy_loop(:-(endif), T, F) :- !,
	(   F > 0 ->
	    F1 is F - 1,
	    conditional_copy_loop(T, F1)
	;   T > 0 ->
	    T1 is T - 1,
	    conditional_copy_loop(T1, 0)
	;   format(user_error, '~N! Unexpected :- endif.~n', []),
	    conditional_copy_loop(0,0)
	).
conditional_copy_loop(:-(if(Condition)), T, F) :-
	(   F =:= 0, condition_is_true(Condition) ->
	    T1 is T + 1,
	    conditional_copy_loop(T1, 0)
	;/* F > 0 or F = 0 and condition is false */
	    F1 is F + 1,
	    conditional_copy_loop(T, F1)
	).
conditional_copy_loop(:-(error(Msg)), T, F) :-
	(   F > 0 -> true
	;   format(user_error, '~N! ~w~n', [Msg])
	).
conditional_copy_loop(Other, T, F) :-
	(   F > 0 -> true
	;   portray_clause(Other)
	),
	conditional_copy_loop(T, F).


As written, this preprocessor copies from one file to another.
That can be a useful way to do it:  it lets you do the translation for
one system in another, more powerful system.
If your Prolog supports term_expansion/2, it is very easy to plug this
conditional processor into that, so no special built-in code is needed.
If your Prolog does not support term_expansion/2, it should.
Lisp without macros is only half a Lisp, as Scheme programmers know.

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

The C pre-processor may not be available because the person using your
Prolog code may not _have_ a C compiler.  (Quintus had customers using
VMS who did not have VAX C.)

And of course the C pre-processor is rather restricted in what it can do.
Writing the pre-processor in Prolog gives you something rather better
suited to Prolog.

For example, I have an
	environment/1
predicate (check the Quintus library) which I have adapted to every
Prolog that I use.  If I define
	condition_is_true(X) :- call(X).
then I can write code like

	:- if(environment(dialect(quintus))).
	float_to_integer(F, I) :- I is integer(F).
	:- endif.
	:- if(environment(dialect(esl))).
	float_to_integer(F, I) :- I is fix(F).
	:- endif.
	:- if((environment(arity(functor(N))), N < 20)).
	:- error('Functor arity bound too small.').
	:- endif.

I can also use that table at run-time to do things like

	write_error_message(M) :-
		telling(Old),
		environment(stream(error(Stream))),
		tell(Stream),
		write(Msg), nl,
		tell(Old).

-- 
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.
Re: Writing portable code.
#1510
Author: Micha Meier
Date: Tue, 29 Sep 1992 12:22
43 lines
2487 bytes
In article 1a3srjINNsgm@darkstar.UCSC.EDU, kjell@cse.ucsc.edu (Kjell Post) writes:
>
>Let me explain by an example what I'm looking for.  I want to write a
>program that works for both SICStus Prolog and Sepia.  The problem is
>that I need to retract all clauses foo/n when the program is started.
>Now, in SICStus the procedure is called retractall/2, but in Sepia it
>is retract_all/2.

It really depends on what you want to do with the source. If you want
to have *exactly* the same source being compiled into different
Prolog systems and work correctly, there is no straightforward
way to do it, as there is no prolog_dialect/1 flag nor other
portable way to identify the Prolog it is running on. The general
idea is then to take the system which is less idiosyncratic about
failing or undefined queries in a compiled file and write code
which is then ignored by this system but executed by the other.
The case of retractall is easy, but sometimes one needs
to redefine a predicate using itself, which may be quite tricky.

If the requirement is less strict, there are other ways:
	- Most prolog systems have an initialization file, e.g. .sicstusrc,
	  .sepiarc, and you can put into it the compatibility stuff.
	  Normally, since Sepia already has a SICStus compatibility library,
	  all you'd have to do is to create a .sepiarc file and put
	  into it the goal
				:- sicstus.

	- You can have a top-level file which is different for every
	  different Prolog dialect. There one can have all sorts
	  of compatibility stuff and the command to compile the
	  common, 'portable' files. I am using this approach for my
	  suite of test programs, as it is better than to rewrite
	  existing, non-portable programs.

	- You can also wait for the standard to come out... :-)

--Micha

---
Micha Meier			------------------------------------------------
ECRC, Arabellastr. 17		The opinions expressed above are private
D-8000 Munich 81		and may not reflect those of my employer.
micha@ecrc.de, Tel. +49-89-92699-108, Fax  +49-89-92699-170
Re: Writing portable code.
#1513
Author: Jose Alberto Fernandez R
Date: Tue, 29 Sep 1992 18:18
113 lines
5133 bytes
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.

   Suppose we use
       :- if(Condition).
       /* stuff */
       :- endif.
   for conditional processing, and let them nest.

   ....... nice coding .....

This code is a very good starting point for the kind of thing I
envisioned. It has some problems though:

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.

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). In that case on could say:

:- if(environment(dialect(quintus))).
:- retract_all_clauses(X) ==> retractall(X).
:- endif.
:- if (environment(dialect(sepia))).
:- retract_all_clauses(X) ==> retract_all(X).
:- endif.

Since then on, the preprocessor would expand every occurrece of
	retract_all_clauses(foo(X,Y))
by 	retract_all(foo(X,Y))
when using SEPIA.

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

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. Although as I just said in a previous message, you
could use other macro processors instead of CPP.

   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 loadder for X11 uses CPP to maccro expand resource files. 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.

   And of course the C pre-processor is rather restricted in what it can do.
   Writing the pre-processor in Prolog gives you something rather better
   suited to Prolog.

   For example, I have an
	   environment/1
   predicate (check the Quintus library) which I have adapted to every
   Prolog that I use.  If I define
	   condition_is_true(X) :- call(X).
   then I can write code like

	.......

   I can also use that table at run-time to do things like

	   write_error_message(M) :-
		   telling(Old),
		   environment(stream(error(Stream))),
		   tell(Stream),
		   write(Msg), nl,
		   tell(Old).


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,
I believe that the programmer should be able to clearly distinguish
between the two. That facilitates the portability of software to
different architectures.

	Jose Alberto.

--
:/       \ Jose Alberto Fernandez R | INTERNET: alberto@cs.umd.edu
:| o   o | Dept. of Computer Sc.    | BITNET: alberto@cs.umd.edu
:|   ^   | University of Maryland   | UUCP: {...}!mimsy!alberto
:\  \_/  / College Park, MD 20742   | 
Re: Writing portable code.
#1523
Author: Stephen J Bevan
Date: Wed, 30 Sep 1992 08:02
30 lines
1803 bytes
In article <ALBERTO.92Sep29141859@karna.cs.umd.edu> alberto@cs.umd.edu (Jose Alberto Fernandez R) writes:
   [ example of a preprocessor written in Prolog deleted ]

   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.

You consider it a problem, I consider it an advantage.


   I don't like that, I believe that the programmer should be able to
   clearly distinguish between the two.

I prefer that one language be used wherever possible.


   That facilitates the portability of software to different
   architectures.

So does using the same language for both tasks.

When all said and done, you prefer the separate language approach and
I prefer the single language approach (preferably using partial
evaluation).  I'm not out to convince anyone that the single language
approach is best and am willing to be convinced by the other side of
the argument, however I've yet to see a technical argument for it
(i.e. something other than "I don't like ...").  Is there a technical
argument of any sort or is this a purely subjective issue?

bevan
Re: Writing portable code.
#1530
Author: Fergus James HENDERSON
Date: Thu, 01 Oct 1992 12:39
38 lines
2354 bytes
bevan@cs.man.ac.uk (Stephen J Bevan) writes:

>When all said and done, you prefer the separate language approach and
>I prefer the single language approach (preferably using partial
>evaluation).  I'm not out to convince anyone that the single language
>approach is best and am willing to be convinced by the other side of
>the argument, however I've yet to see a technical argument for it
>(i.e. something other than "I don't like ...").  Is there a technical
>argument of any sort or is this a purely subjective issue?

There *is* a technical argument for using a separate preprocessing language.
Often it is not just names (retractall vs. retract_all) that differ between
compilers, sometimes it is the very syntax of the language itself that varies.
In these cases you need the two-level approach so that you can prevent
the compiler from attempting to parse something that it cannot possibly
understand.

Fortunately for Prolog users, the syntax of Prolog is very simple and fairly
standardized. Most of the various language extensions are confined to
Prolog's ?- or :- directives, the Prolog equivalent to C's #pragma.
However even amoung these there are some cases when one might require
the two-level approach. For example:

#if nu_prolog
    ?- foo(X) when X.	% other systems may choke on this
    foo(X) :- foo1(X).
#else
    foo(X) :- (var(X) -> error('argument to foo is not bound') ; foo1(X)).
#endif

I'm sure there are many other such examples. Another that comes
to mind is type declarations.

-- 
Fergus Henderson             fjh@munta.cs.mu.OZ.AU      
This .signature virus is a self-referential statement that is true - but 
you will only be able to consistently believe it if you copy it to your own
.signature file!
Re: Writing portable code.
#1550
Author: Richard A. O'Keefe
Date: Tue, 06 Oct 1992 04:31
189 lines
8984 bytes
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.
Thread Navigation

This is a paginated view of messages in the thread with full content displayed inline.

Messages are displayed in chronological order, with the original post highlighted in green.

Use pagination controls to navigate through all messages in large threads.

Back to All Threads