Category Archives: logtalk

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.


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.


Prolog compilers are too permissive

Prolog compilers are too permissive, too forgiving… resulting in sloppy programming. Experienced programmers may shake off guilty feelings convincing themselves that’s only run-once code that nobody is going to reuse, or port, or maintain. Or that’s really not their fault. After all, if the Prolog compiler doesn’t complain and the application appears to run, why care? Novice programmers never notice until they decide to switch Prolog compilers. Or when someone comes along and asks “what if” when trying to help them debugging or porting their applications.

Consider a simple example, the arg/3 built-in predicate. Some popular Prolog compilers have long interpreted a negative term argument position as a failure rather than a programming error. When I complained (I used to do that a lot to Prolog implementers) the reply was something along the lines “we agree but our users would be upset if we break compatibility with their applications”. You can easily find similar examples. Just dig into your memories. E.g. do you remember when “logical update semantics” come along all those applications relying on “immediate update semantics” suddenly broke?

Recently I ported, or tried to port, well known Prolog libraries and interesting Prolog applications to Logtalk, both to allow running these libraries and applications in most Prolog compilers and for testing the Logtalk automatic compilation of Prolog modules as Logtalk objects (oh the pain I choose to inflict upon myself!). Common sins are missing discontiguous/1 and multifile/1 directives, non-declared dependencies, i.e. missing use_module/1 or use_module/2 directives, arbitrary goals used as directives (instead of disciplined use of the initialization/1 directive), duplicated predicate exports (specially when re-exporting predicates from imported modules), operator overdoses, term expansion clauses defined in the same file that is going to be term expanded (suddenly predicate definition order in a source file is important!), not to mention code relying on assumptions that are specific to a Prolog compiler or an operating-system. Prolog compilers should warn the users about these (and other) issues.

Another problem is hidden dependencies in Prolog “modes”. Some Prolog compilers support an “iso” mode (that usually is not the default) and some other modes for backwards compatibility. Guess what happens when users try to reuse libraries developed for the same Prolog compiler but using different modes. Or when users startups the Prolog compiler in the wrong mood… err mode. If they are lucky, they will get a stream of compilation errors.

This is a vicious circle. Prolog implementers are afraid to make their compilers more strict because they don’t want to break existing code or upset users. Users enjoy a permissive and flexible programming environment, even if they risk shooting themselves in the foot, unconsciously write non portable code, and have trouble listing their own code dependencies without using a cross-reference tool; they have little incentive and see no rewards in writing more portable and robust code.

Portability is not the only victim here. Is hard to do static code analysis (you want your applications to run faster and use less memory, don’t you?) in this mix of explicit and implicit programming assumptions. ISO standardization is another victim as Prolog implementers and library developers get defensive arguing that the required changes are only of interest to ISO nitpicks, failing to see the forest behind their own tree.

This post is a bit harsh, I know. But wake-up calls sometimes need harsh voices to complement more carrot-like incentives and initiatives. Please don’t shoot the messenger.


Using Logtalk to run Prolog module code in compilers without a module system

Logtalk can compile most Prolog modules as objects. This is accomplished by recognizing and parsing a common subset of Prolog module directives. For simple module code, it suffices to change the file name extensions from .pl to .lgt and compile the files as usual using the logtalk_load/1-2 built-in predicates. For modules that use proprietary predicates, directives, and syntax, some changes to the original code may be necessary.

As an example, assume that we want to use the SWI-Prolog modules lists, pairs, oset, and ordsets in GNU Prolog, a compiler that doesn’t support a module system. After making a copy of the original files and renaming their extensions to .lgt, we will need to remove a few specific, proprietary SWI-Prolog bits. First, we need to comment out all occurrences of the directive:

:- set_prolog_flag(generate_debug_info, false).

Second, we need to comment out in lists.lgt the calls to the must_be/2 predicate and the line:

:- use_module(error, [must_be/2]).

Still in lists.lgt we will need to replace the call to the succ/2 by:

M is N + 1,

Third, Logtalk doesn’t support the use_module/1 directive, requiring instead the use of the use_module/2 directive. Thus, we need to replace the directive:

:- use_module(library(oset)).

with the directive:

:- use_module(oset,
        [oset_int/3, oset_addel/3, oset_delel/3,
         oset_diff/3, oset_union/3]).

Finally, is_list/1 is a built-in predicate in SWI-Prolog but not in GNU Prolog. Logtalk provides its own portable versions of the is_list/1, succ/2, and must_be/2 predicates but let’s leave them out for now, for the sake of simplicity. After saving our changes, we are ready for a quick experiment:

$ gplgt
...
Logtalk 2.36.0
Copyright (c) 1998-2009 Paulo Moura
...
GNU Prolog 1.3.1
By Daniel Diaz
Copyright (C) 1999-2009 Daniel Diaz
| ?- {lists, pairs, oset, ordsets}.
...
yes
| ?- pairs::map_list_to_pairs(lists::length,[[1,2],[a,b,c],[X]],Pairs).
 
Pairs = [2-[1,2],3-[a,b,c],1-[X]]
yes

In SWI-Prolog the equivalent call would be:

$ swipl
...
Welcome to SWI-Prolog (Multi-threaded, 32 bits, Version 5.7.8)
...
?- pairs:map_list_to_pairs(length,[[1,2],[a,b,c],[X]],Pairs).
Pairs = [2-[1, 2], 3-[a, b, c], 1-[X]].

So, all done? Not quite. Different Prolog compilers provide different sets of built-in predicates. Module libraries use those built-in predicates. Thus, porting module code must also check for used built-in predicates. Fortunately, that’s quite easy in Logtalk: we simply compile the files with the portability compiler flag set to warning. In the case of our example, there is no problem for GNU Prolog but if we try to load our files in e.g. B-Prolog (another compiler without a module system) we will find that the lists module calls a memberchk/2 predicate that is not built-in in this compiler.

The porting process is not as straightforward as we may hoped for, but nothing is really straightforward when dealing with Prolog portability issues. The code changes needed are usually simple to apply as the example above illustrates. A small price to pay when porting module code. I’m sure you appreciate the irony of using Logtalk to run Prolog module code in module-less Prolog compilers. The subset of module directives recognized by Logtalk is enough for dealing with most Prolog module libraries. This subset could also provide a basis for a Prolog module standard based on current practice but that’s a topic for another post.

P.S. The predicate succ/2 is defined in the Logtalk library object integer. An equivalent predicate to is_list/1 is defined in the Logtalk library object list, which also defines a memberchk/2 predicate. Moreover, each Logtalk library object that defines a type contains a definition for a check/1 predicate that plays the same role as the SWI-Prolog library predicate must_be/2.


Logtalk 2.36.0 released

I released Logtalk 2.36.0 yesterday. The biggest news is support for settings files. At startup, Logtalk looks for and loads a settings.lgt file found in the startup directory. If not found, Logtalk looks for a loads a settings.lgt file in the Logtalk user folder. These settings files allows users to customize Logtalk without forcing them to edit the back-end Prolog configuration files, which often change between releases. This allows users to easily update to a new Logtalk version without the tedious work of reapplying changes to the default values of compiler flags to the new versions of the configuration files. Updated scripts and installers automatically will take care, for future releases, of preserving any existing settings file found when updating the Logtalk user folder.

Although the new settings files feature is easy to describe, a lot of work went into implementation and testing, thanks to the lack of Prolog standards for operating-system access. This includes basic functionality such as finding out the startup directory, opening a file in the current directory, and accessing operating-system environment variables. Something that should be implemented and tested during an afternoon, took me one week of work. One week that would be better spent working on e.g. the Logtalk libraries. In the end, I got settings file working for most back-end Prolog compilers on POSIX systems and, for some of them, also on Windows. Not ideal but hard to do better giving the back-end Prolog compiler limitations.

Lack of feature-parity between back-end Prolog compilers is always a problem. It turns what should be simple documentation in long lists of exceptions. Some users, completely oblivious of the nightmare that is writing portable Prolog code, end up blaming Logtalk for a limited feature set that pales when compared with recent programming languages. I’m quite tired of dealing with this portability problems. Therefore, future Logtalk releases will cut down on the number of compatible Prolog compilers. Hopefully, this will speed up future development and will enable more straightforward implementations of new functionality.


A PhD thesis you don’t want to miss

Jan Wielemaker, SWI-Prolog main developer, kindly sent me a paper copy of his PhD thesis. You may grab a PDF version at the following URL:

http://www.swi-prolog.org/Publications.html

This is a PhD thesis you don’t want to miss. It describes Jan’s outstanding work developing SWI-Prolog as a top-notch Prolog system and applying logic programming to solve large-scale problems. Hats off to you Jan. Thanks for sharing your hard work with all of us.

P.S. I love the thesis cover.


CLPSE 2009 workshop

I’m co-organizing, together with Ulrich Neumerkel, an ICLP’09 workshop on software engineering:

http://logtalk.org/workshops/clpse2009/

If you’re working in any topic related to software engineering in the context of logic programming applications or developing a sizable logic programming application consider submitting your work to the 4th International Workshop on (Constraint) Logic Programming and Software Engineering. See the URL above for details.


Using both object proxies and regular objects

During the last three days I was working with Artur Miguel Dias, the author of CxProlog, rewriting P-FLAT, a Prolog toolkit for teaching Formal Languages and Automata Theory, as a Logtalk application, tentatively named L-FLAT. We made great progress and the final version of L-FLAT is going to be smaller, more portable, and both simpler to maintain and extend than the original plain Prolog application. One of our goals is to allow users to use both object proxies and regular objects when representing Turing Machines, Pushdown Automata, Context Free Grammars, and other concepts and mechanisms supported by L-FLAT. This flexibility allows users to represent e.g. a simple Finite Automaton with a small number of states and transitions as an object proxy (i.e. as a Prolog compound term; see my previous post) that might be entered interactively at the command-line:

fa(fa1,
    1,                                    % initial state
    [1/a/1, 1/a/2, 1/b/2, 2/b/2, 2/b/1],  % transitions
    [2]                                   % final states
).

It also allows users to represent e.g. a complex Turing Machine with dozens of transitions as a regular object, defined in a source file:

:- object(copyTM,
    instantiates(tm)).
 
    initial(q0).
 
    transitions([
        q0/'B'/'B'/'R'/q1,
        q1/a/'X'/'R'/q2,   q1/b/'Y'/'R'/q5,   q1/'B'/'B'/'L'/q7,
        q2/a/a/'R'/q2,     q2/b/b/'R'/q2,     q2/'B'/'B'/'R'/q3,
        q3/a/a/'R'/q3,     q3/b/b/'R'/q3,     q3/'B'/a/'L'/q4,
        q4/a/a/'L'/q4,     q4/b/b/'L'/q4,     q4/'B'/'B'/'L'/q4,
                           q4/'X'/'X'/'R'/q1, q4/'Y'/'Y'/'R'/q1,
        q5/a/a/'R'/q5,     q5/b/b/'R'/q5,     q5/'B'/'B'/'R'/q6,
        q6/a/a/'R'/q6,     q6/b/b/'R'/q6,     q6/'B'/b/'L'/q4,
        q7/'X'/a/'L'/q7,   q7/'Y'/b/'L'/q7
    ]).
 
    finals([]).
 
:- end_object.

Using both object proxies and regular objects requires that all predicates that expect e.g. a Regular Expression to accept both an object identifier and an object proxy (a compound term) as argument. This might sound as an additional hurdle until you realize that an object proxy is also an instantiation of the identifier of a parametric object. As long as the parametric object defines the straightforward predicates that give access to its parameters, everything will work as expected. But how do we relate the regular objects and the parametric objects? Easy. If you want to represent e.g. some finite automata as instances of class fa and other finite automata as object proxies, simply define a parametric object as an instance of class fa. The parametric object parameters will be the properties that might be unique for a specific finite automaton. The parametric object define the predicates that give access to these properties. These predicates are the same that are used in class fa in the definition of all predicates that need access to finite automaton properties:

:- object(fa(_Initial, _Transitions, _Finals),
    instantiates(fa)).
 
    initial(Initial) :-
        parameter(1, Initial).
 
    transitions(Transitions) :-
        parameter(2, Transitions).
 
    finals(Finals) :-
        parameter(3, Finals).
 
:- end_object.

Thus, using both object proxies and regular objects becomes trivial, fully transparent, and just a matter of defining the necessary parametric objects.


Efficient representation of data objects

Some Logtalk applications need to represent large numbers of immutable objects. These objects typically represent data that must be validated or used for data mining. This kind of data is often exported from databases and must be converted into Logtalk objects for further processing. The application logic is usually represented by a set of hierarchies with the data objects at the bottom. Although data conversion is most of the time easily scriptable, the resulting objects can take up significantly more space than a straightforward representation as Prolog facts. Logtalk object representation is simply not optimized for this kind of objects. Fortunately, this is one of those cases where you can eat the cake and keep it. The solution is to represent data as compact Prolog facts and to interpret those facts as object proxies. An object proxy is simply a Prolog compound term that can be interpreted as a possible instantiation of the identifier of a parametric object.

For a toy example, assume that your data represents geometric circles with attributes such as the circle radius and the circle color:

% circle(Id, Radius, Color)
circle('#1', 1.23, blue).
circle('#2', 3.71, yellow).
circle('#3', 0.39, green).
circle('#4', 5.74, black).
circle('#5', 8.32, cyan).

We can define the following parametric object for representing circles:

:- object(circle(_Id, _Radius, _Color)).
 
    :- public([
        id/1, radius/1, color/1,
        area/1, perimeter/1
    ]).
 
    id(Id) :-
        parameter(1, Id).
 
    radius(Radius) :-
        parameter(2, Radius).
 
    color(Color) :-
        parameter(3, Color).
 
    area(Area) :-
        ::radius(Radius),
        Area is 3.1415927*Radius*Radius.
 
    perimeter(Perimeter) :-
        ::radius(Radius),
        Perimeter is 2*3.1415927*Radius.
 
:- end_object.

The circle/3 parametric object provides a simple solution for encapsulating a set of predicates associated with a given compound term. But how do we perform computations with the object proxies? We cannot send messages to Prolog facts! Or can we? Logtalk provides a handy notation for working with object proxies. For example, in order to construct a list with all circle areas we can write:

| ?- findall(Area, {circle(_, _, _)}::area(Area), Areas).
 
Areas = [4.75291, 43.2412, 0.477836, 103.508, 217.468]
yes

The message sending control construct, ::/2, accepts as receiving object the term {Proxy}. Logtalk proves Proxy as a Prolog goal and, if successful, sends the message to the resulting term. Backtracking over the Proxy goal is supported, allowing all matching object proxies to be processed by a simple failure-driven loop (as implicit in the findall/3 call above).

You can find the full source code of this example in the current Logtalk distribution. In real applications, data objects, represented as object proxies, are tied to large hierarchies representing both knowledge about data and how to reason with it.