Monthly Archives: July 2009

ICLP 2009 invited talk on Logtalk

The slides I used on my ICLP 2009 invited talk on Logtalk are available at:

http://logtalk.org/papers/iclp2009/logtalk_iclp2009.pdf

My thanks to Patricia Hill and David S. Warren for the invitation. A special thanks for all of you that attended the invited talk. Hope you enjoyed it. I had a great time at Pasadena, meeting old friends and making new ones. The only puzzling thing was people complains about the temperature in the conference rooms, which I found quite comfortable. But that was just me ;-) There are some interesting news from the ISO Prolog standardization meeting but that’s a topic for another post.


Loading is not importing!

Recently, I posted some rumblings about my experience compiling Prolog modules as Logtalk objects. One of my pet peeves with Prolog module systems and Prolog module code is the unfortunate mix up between loading and importing. Is quite simple really, repeat after me:

Loading is not importing!

If a user wants to load a module, the ensure_loaded/1 directive shall be used. If the user want to import the public predicates of a module, the use_module/1-2 directives shall be used. Sadly, this basic distinction between these two orthogonal operations is not enforced by most Prolog compilers with a module system (despite some advice found in Prolog user manuals). Moreover, the ensure_loaded/1 directive shall only be used outside modules. Its semantics should simply be: load a file if not already loaded. There is no need for this directive to be the jack-of-all-trades. A simple test: if your Prolog compiler complains when you load two Prolog source files defining two different modules exporting the same predicate, then your Prolog compiler is broken. Complaining about conflicting imports only makes sense when importing. Otherwise a library developer would need to be omniscient about every other library developer work. Module encapsulation purpose is to avoid predicate name conflicts in the first place. Auto-importing public predicates when loading is equivalent to putting holes in module encapsulation. For what purpose? Is really that much trouble to use the use_module/1-2 directives for importing? Sure, these directives also load a file if not already loaded. They must. After all, we are saying that we want to use a module! That doesn’t mean that the ensure_loaded/1 directive should work in retribution as a use_module/1-2 directive in disguise!

As the bible says, why complicate implementation, complicate documentation, complicate semantics? In order to perpetuate mix-ups? To ruin encapsulation goals? Just to cope with sloppy user programming?


Logtalk 2.37.2 released

I released Logtalk 2.37.2 a couple of days ago. This is the third incremental release of the 2.37.x version. A total of 37 major releases, 122 when including both major and minor releases, since I started the Logtalk project in January of 1998 (the number 2 means this is the second generation of the Logtalk language; generation 3 is coming in a not so distant future). Release 2.37.3 is already being developed. This is your typical example of the open source mantra release often, release early.

The wide Prolog compatibility goals of Logtalk means that there is really no end in sight for Logtalk development. Sometimes this scares me. At other times, it pushes me out of bed. It also means that an wealthy number of Prolog compilers are alive and kicking, continuously being improved. Many of the changes in this and past releases are Prolog compatibility updates. Lack of strong standardization only complicates matters.

This release takes Logtalk support for compiling Prolog modules to a new level. This support is important for a number of reasons. First, it shows that Logtalk is not only a superset of plain Prolog but also of Prolog plus modules. Within reasonable terms, of course, giving that Prolog module dialects often remind me of the Babel tower tale. Second, compiling a module as a Logtalk object helps to identify potential problems when porting Prolog code to Logtalk. Testing of this feature include compiling, or trying to compile, several Prolog modules libraries and non-trivial Prolog module applications such as TopLog and ClioPatria. Results are good despite some unfortunate problems in the original Prolog module code. Third, all the trouble in implementing this feature helps to improve the Logtalk code base, making it more robust, allowing it to cope with a diversity of programming practices. Fourth, it helps me to better understand the subtleties of specific Prolog module systems, something that often is not easy to learn just by sitting down and reading user manuals. One always learns good bits to adapt and pitfalls to avoid when studying other people’s code.

Hope you enjoy this new release.

P.S. Is great to finally get some free time to post some thoughts on Logtalk development after a very tiresome teaching semester. I keep dreaming about doing Logtalk development full time. Maybe one of these days…


Meta-predicate semantics

Meta-predicates allow reusing of programming patterns. Encapsulating meta-predicates in Prolog modules or Logtalk objects allows client modules and objects to reuse predicates customized by calls to local predicates. Nevertheless, meta-predicate semantics differ between Prolog and Logtalk. Logtalk meta-predicate semantics are quite simple:

Meta-arguments are always called in the meta-predicate calling context. The calling context is the object making the call to the meta-predicate.

Prolog semantics are similar but require the programmer to be aware of the differences between implicit and explicit module qualification. Consider the following meta-predicate library:

:- module(library, [my_call/1]).
 
:- meta_predicate(my_call(:)).
my_call(Goal) :-
    write('Calling: '), writeq(Goal), nl, call(Goal).
 
me(library).

A simple client could be:

:- module(client, [test/1]).
 
:- use_module(library, [my_call/1]).
 
test(Me) :-
    my_call(me(Me)).
 
me(client).

A simple test query:

?- client:test(Me).
Calling: client:me(_G230)
Me = client.

This is the expected result, so everything seems nice and clear. But consider the following seemingly innocuous changes to the client module:

:- module(client, [test/1]).
 
test(Me) :-
    library:my_call(me(Me)).
 
me(client).

In this second version we use explicit qualification in order to call the my_goal/1 meta-predicate. Repeating our test query gives:

?- client:test(Me).
Calling: library:me(_G230)
Me = library.

In order to understand this result, we need to be aware that the :/2 operator both calls a predicate in another module and changes the calling context of the predicate to that module. The first use is expected. The second use is not obvious, is counterintuitive, and often not properly documented. We can, however, conclude that the meta-predicate definition is still working as expected as the calling context is set to the library module. If we still want the me/1 predicate to be called in the context of the client module instead, we need to explicitly qualify the meta-argument by writing:

test(Me) :-
    library:my_call(client:me(Me)).

This is an ugly solution but it will work as expected. Note that the idea of the meta_predicate/1 directive is to avoid the need for explicit qualifications in the first place. But that requires using use_module/1-2 directives for importing the meta-predicates and implicit qualification when calling them.

Explicit qualification is not an issue in Logtalk, nor does it change the calling context of a meta-predicate. Explicit qualification of a meta-predicate call sets where to start looking for the meta-predicate definition, not where to look for the meta-arguments definition.

I suspect that the contrived semantics of the :/2 operator is rooted in optimization goals. When a directive use_module/1 is used, most (if not all) Prolog compilers require the definition of the imported module to be available (thus resolving the call at compilation time). However, that doesn’t seem to be required when compiling an explicitly qualified module call. For example, using SWI-Prolog 5.7.10 or YAP 6.0, the following code compiles without errors or warnings (despite the fact that the module xpto doesn’t exist):

:- module(foo, [bar/0]).
 
bar :-
    xpto:blabla.

Thus, in this case the xpto:blabla call is resolved at runtime. In our example above with the explicit call to the my_call/1 meta-predicate, the implementation of the :/2 operator propagates the module prefix to the meta-arguments. Doing otherwise would imply knowing at runtime the original module containing the call, information that most Prolog compilers don’t keep.

In the case of Logtalk, the execution context of a predicate call always includes the calling context, allowing simpler meta-predicate semantics (and much more). Moreover, the Logtalk compiler doesn’t need to know that we’re calling a meta-predicate when compiling source code. This allows client code to be compiled independently of library code. Meta-predicate information is either used at compile time when static binding is possible or at runtime. In the second case, the caching mechanism associated with dynamic binding ensures that the necessary computations to know which arguments are meta-arguments are only performed once. There is, of course, a small performance penalty in carrying predicate execution context. I argue that’s a small price to pay in order to simplify meta-predicate semantics.