Monthly Archives: March 2011

A more sane implementation of the term-expansion mechanism

One of the oldest and more useful feature found in most Prolog compilers is the term-expansion mechanism. Acting as a souped-up pre-processor, defining term- and goal expansion clauses allows us to rewrite terms and goals as their are read from a source file and apply all kinds of transformations. This made term-expansion the preferred mechanism for implementing all sorts of extensions to Prolog.

You may be wondering if the Logtalk compiler is also implemented using the term-expansion mechanism. The answer is no. One of the main reasons is that the term-expansion mechanism is not implemented by all Prolog compilers. Among those that do implement term-expansion, there are also some minor (and not so minor) syntax and semantics differences. The real trouble with term-expansion results, however, from its usual implementation and usage patterns, combined with module features, not from those portability issues.

The usual pattern when implementing some Prolog extension using the term-expansion mechanism is to define a module, relying on term- and goal-expansion clauses to compile to standard Prolog code the syntactic elements defined by the extension. As the recommended way of using modules is to simply use a use_module/1 directive to make the module resources available in your current context (how convenient and easy is to shoot yourself in the foot… but I digress), the term_expansion/2 and goal_expansion/2 predicates are declared as multifile predicates, with the clauses ending up either on the user module or on a special system module. Either way is fine until you try to use more than one module that defines term_expansion/2 clauses. The Prolog compiler will try term_expansion/2 clauses until it finds one that succeeds for the term being expanded or until there are no more clauses left to try (the goal_expansion/2 predicate, however, is called recursively until no more goal-expansion clauses apply). Assuming that no two Prolog modules want to expand the same term (one can always hope), there should no problem. Sure, there might be a performance issue due to trying and failing term_expansion/2 clauses until the correct one is selected. But what happens if two modules compete for expanding the same term? Say, the end_of_file term. Well, if the module developers are not aware of the problem, one of them will likely lose. Each one? That will depend on the modules loading order. The usual workaround is to define term_expansion/2 clauses that perform their magic using side-effects to record the results and fail when done. The failure leads to backtracking, giving other term_expansion/2 clauses a chance to be applied. Ugliness of using side-effects apart, there is a more sane way of implementing and using the term-expansion mechanism.

One of the advantages of not being first (to design and try a new mechanism) is that you can learn from both the successes and mistakes of others before you. You also have a significant advantage by not having to care about breaking backward compatibility. The implementation of term-expansion in Logtalk differs from the implementation found on most Prolog compilers in two crucial aspects.

First, Logtalk doesn’t support using the term- and goal expansion clauses defined in an object (or source file) to expand the object (or source file) itself. It’s already bad enough that some predicate directives must precede the compilation of calls to those predicates. But having the result of the compilation of an object (or source file) depend on the order of the predicate definitions in the same object (or source file) feels plain wrong. Specially as it’s an exception to the way both Logtalk and Prolog compilers work. Just imagine being forced to define all predicates called by a predicate before it could be defined. It will be painful. It would also be impossible in some cases. Yet, well know Prolog extensions do just that, throwing portability down the drain (not all Prolog compilers make predicate definitions visible and usable, as soon as they are compiled, without waiting for the rest of the source file to be compiled…).

Second, term- and goal-expansion clauses are defined local to the object where they are defined. It’s possible to add those clauses to the pseudo-object user (Logtalk supports multifile object predicates). But that is neither necessary or advised. Do we really want to put all our eggs… err… term-expansion hooks in the same basket?

So, what’s the magic incantation to use term-expansion in Logtalk? Easy. Objects containing clauses for the term- and goal expansion predicates are known as hook objects. When compiling a source file, you use a compiler option to state which hook object should be used to term-expand the source file. For example, assuming a hook object named my_expansion we could write:

| ?- logtalk_load(source_file, [hook(my_expansion)]).

This compiler option also provides a nice solution for combining different hook objects in order to expand the same source file. You simply define another hook object that calls the term- and goal-expansion predicates defined in the base hook objects. This allows fine control on how the different expansions will work together. For example, we can define a pipeline of goal-expansions. Or apply all term-expansions in parallel and combine the resulting terms. For some sample code, check the expanding example in the current Logtalk distribution. Or browse the example source code on-line:

Even when a source file is to be expanded using a single hook object, we can be ensured that the compilation time will not be adversely affected by other hook objects that might be loaded at the time of the compilation. The code also becomes easier to debug as only the relevant term-expansion clauses will be in focus.

P.S. It’s worth noting that the Logtalk term-expansion implementation and recommended usage patterns can be easily adopted by Prolog module systems. In that regard, it’s also worth noting that some Prolog compilers, e.g. SICStus Prolog and Ciao Prolog, already include improved implementations of term-expansion mechanisms.

Coding guidelines and the spaces-versus-tabs holy war

Logtalk, like most programming languages, defines a set of coding guidelines that aim to improve the quality and readability of source code:

These coding guidelines are also mandatory for contributions to the Logtalk distribution. Most guidelines in the above document are pretty consensual but one of them, source code layout and indentation, always stirs a lot of discussion:

Format your source file using tabs, not spaces.

Search the web and you can easily find passionate texts defending the use of either tabs or spaces for source code layout and indentation. Recently, I was commenting on a paper on Prolog coding guidelines that stated:

2.1 Indent with spaces instead of tabs.

You cannot normally predict how tabs will be set in the editors and browsers others will use on your code. Moreover, mixing tabs and spaces makes searches and substitutions more problematic. All in all, it is better not to use tabs altogether: almost any editor can be set to substitute spaces for tabs automatically when saving your file.

Take the first sentence. It completely misses the point of using tabs instead of spaces. Try this. Open any of the many examples distributed with Logtalk in your favorite text editor. The example source code is formatted using tabs. Next go to your text editor preferences and adjust the tab size to your liking. I prefer a tab size equivalent to 4 spaces. You may prefer a tab size equivalent to 2 spaces. Or 3 spaces. Or 6 spaces. No matter your choice, the code will remain perfectly indented. That means that I’m not imposing on you my indentation preferences. If someone gives you a file indented using spaces, say, where an indentation level is 2 spaces, and you prefer to use 4 spaces, your preference will be ignored. You will need to edit the file, i.e. change it, even if only for you to feel comfortable reading/studying the source code.

If you think that my argument is only meaningful for text editors but not for browsers, well, think again. I have written (and tested!) support for syntax highlight of Logtalk source code for Pygments, GeSHi, Highlight, Source-highlight, and SyntaxHighlighter. If you’re publishing source code on a web page, a blog, or a wiki, chances are that you will be using one of these syntax highlighters. As you already guessed, they allow you to set your tab preferences. Never found a problem due to the use of tabs instead of spaces.

I have also written (and tested!) Logtalk syntax highlight support for several text editors, including TextMate, SubEthaEdit, Emacs, Vim, Kate, Gedit (i.e. GtkSourceView), NEdit, and jEdit. Again, never found a problem due to the use of tabs instead of spaces. This, I concede, may not have always been true in the past but, nowadays, using spaces for indentation is just a big waste of space. And obnoxious to all people you may share your code with that don’t necessarily share with you the same preferences for the size of an indentation level.

As this post is about a holy war, it’s mandatory and good netiquette that I finnish it with a suitable insult or inflammatory remark to the other side. So, death to the tab infidels! May your spacebar burn in hell!

Writing unit tests in Logtalk

Logtalk provides simple but portable support for writing unit tests. This support is provided by a library object, lgtunit, inspired on the works of Joachim Schimpf (ECLiPSe library test_util) and Jan Wielemaker (SWI-Prolog plunit package). You can load it using the goal:

| ?- logtalk_load(library(lgtunit)).

The interface of the lgtunit object is quite simple and can be found e.g here:

Most of the examples found on the current Logtalk distribution include a set of unit tests, together with a handy loader file named tester.lgt. Typically, this loader file loads the code you want to test, the unit test framework, the unit tests themselves, and send a run/0 message to the unit test objects. Check, for instance, the example:

In order for you to write your own unit tests, start by defining objects extending the lgtunit object. These objects must be compiled using the option hook(lgtunit). For example:

| ?- logtalk_load(my_tests, [hook(lgtunit)]).

After successful compilation and loading of your unit test objects, you can run them by typing:

| ?- my_tests::run.

Logtalk unit tests can be written using any of three different dialects. The most simple dialect is:

test(Test) :- Goal.

This dialect allows the specification of tests that are expected to succeed. Tests that are expected to fail can be written by using the \+/1 built-in predicate. However, tests that are expected to throw an error cannot be specified. A second dialect overcomes this restriction:

succeeds(Test) :- Goal.
fails(Test) :- Goal.
throws(Test, Error) :- Goal.

This is a straightforward dialect. For succeeds/1 tests, Goal is expected to succeed. For fails/1 tests, Goal is expected to fail. For throws/2 tests, Goal is expected to throw Error. A third supported dialect is:

test(Test, Outcome) :- Goal.

In this case, Outcome can be the atom true, the atom fail, or the exception term that the test is expected to throw. In all cases, Test is an atom, uniquely identifying a test. Simply use the dialect that is the best fit for your application.

That’s all. Happy testing!

Note: when using the <</2 control construct to access and test an object internal predicates, make sure that the objects are compiled with the context_switching_calls flag set to allow.