[mercury-users] term_expansion (was Re: Microsoft Common Lisp?)

Fergus Henderson fjh at cs.mu.oz.au
Mon Feb 17 22:43:53 AEDT 1997

Peter Schachte wrote:
> Sorry, but I just couldn't let this statement go unquestioned....
> On Thu, 13 Feb 1997, Fergus Henderson wrote:
> > Mercury does not have ISO Prolog's preprocessing (term_expansion),
> > although it's quite easy to do preprocessing as a separate step, and
> > the GNU make based Mercury build environment can handle this quite easily.
> They handle it, but I certainly wouldn't say they do it "quite
> easily."  The absence of such transformations, despite the many
> obvious applications, serves as evidence that it's not so easy.

I don't find this evidence convincing.  The absence of such
transformations could instead be evidence that either (a) people just
aren't aware of how to do it, or (b) people who are using Mercury
aren't interested in them, or (c) that people who are interested in
them aren't using Mercury.

> There
> are three ways that Prolog's term_expansion makes this easier than it
> is in Mercury:
>     1.  Prolog takes care of reading the source file for you, and
> 	there's no need to write a transformed file, either.  Mercury
> 	could fix this with a higher-order library predicate that
> 	takes two file names and a predicate, and reads the first file
> 	into a data structure, passes this data structure to the
> 	predicate, gets a transformed data structure back from the
> 	predicate, and then spits it out into the second file.

Well, it's not part of the standard library, but we have (since release 0.6)
provided source code for doing this in the `samples' directory that comes
with the Mercury distribution.

>     2.  term_expansion doesn't require users to monkey with Makefiles
> 	to get their programs to work correctly.  There are some
> 	subtleties for Prologs that support separate compilation of
> 	source files to object files (as Mercury does), though.  I
> 	don't think this should be underemphasised.  Right now,
> 	Mercury will generate your Makefiles for you, and will even
> 	keep the dependencies correct.  This is a *major* improvement
> 	over C.  But Mercury requires the implementor of a language
> 	extension to have to explain to their users that they'll have
> 	to name their source files with a different extension than .m,
> 	and then edit all their Makefiles to put in a rule that
> 	transforms files with this extension into .m files by invoking
> 	a translator.  This throws away the hard-won simplicity of
> 	Mercury's module/mmake system for language extensions.

I consider the requirement that files that use language extensions have
have different extension to be a feature, not a bug.  The different
extension gives maintenance programmers a clue that something funny
is going on here.

>     3.  term_expansion handles, though not a smoothly as one might
> 	like, the use of multiple languages extensions in the same
> 	file, while simple make rules won't.

Sure they will. 

Here's one approach.  Supposing you have different language extensions
called "foo" and "bar".  Then your Mmakefile can have rules for each of

	%: %.foo
		foo_preprocessor $< > $@

	%: %.bar
		bar_preprocessor $< > $@

Here `$<' and `$@' are builtin Make variables for the source file
and target file respectively.  The `%' is a pattern-matching character.

Then, if you name a file `myfile.m.foo.bar', it will be run through
bar_preprocessor, and then through foo_preprocessor, and then through
the Mercury compiler.

There is one problem -- GNU Make doesn't handle chaining of multiple
pattern rules like this completely automatically.  You need to
specify the intermediate file `myfile.m.foo' explicitly somewhere
in the Makefile, e.g.

	targets: myfile.m.foo

Annoying, I guess, but it doesn't seem like a serious problem.

Oh, hang on -- I just tried it, and it seems there is another problem.
The other problem is a bug in GNU Make related to mixing the use of
`.SUFFIXES' and pattern rules.  I've reported the bug.  If necessary,
we could work around it without too much difficulty.

In the mean time, you will have to use

	%.m: %.foo
		foo_preprocessor $< > $@

	%.foo: %.bar
		bar_preprocessor $< > $@

instead.  That also avoids the requirement to explictly name intermediate

Hang on, here's another idea.

	PREPROCESSOR = foo | bar

	%.m: %.pp
		cat $< | $PREPROCESSOR > $@

Scrap the previous approaches, this one seems simpler.
Now all you need to do if you want to add another preprocessor is
to add it to the definition of `PREPROCESSOR':

	PREPROCESSOR = foo | bar | baz

There you go.  I knew simple make rules could handle it.

> The Mercury solution will work, but it's certainly nowhere near as
> easy for the implementor (and documenter!) of such a translator, nor
> for the user, as Prolog's term_expansion approach.

Using preprocessing to invent special-purpose languages or language
extensions is fun, and it can sometimes make certain things much
easier, but it can also make the source code more difficult to
understand, and it's just one more thing that a maintenance programmer
must learn before they can start being productive.  Using preprocessors
is already pretty easy in Mercury; I'm not convinced that making it
even easier would actually improve overall productivity.

> On the other hand, last I heard ISO Prolog didn't have term_expansion
> either, although most Prologs do.

Oh, yes, you're right, although most Prologs have it, term_expansion
isn't part of ISO Prolog.  Thanks for the correction.

Fergus Henderson <fjh at cs.mu.oz.au>   |  "I have always known that the pursuit
WWW: <http://www.cs.mu.oz.au/~fjh>   |  of excellence is a lethal habit"
PGP: finger fjh at         |     -- the last words of T. S. Garp.

More information about the users mailing list