Tag Archives: modules

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.


Prolog modules madness

Try the following experiment in a Prolog compiler supporting modules such as SWI-Prolog or YAP. Define two modules that export the same predicate and try to load them. For example, assume a file m1.pl with content:

:- module(m1, [p/1]).
 
p(X) :- clause(X, true).
 
:- dynamic(a/1).
a(one). a(two). a(three).

and a file m2.pl with content:

:- module(m2, [p/1]).
 
p(X) :- clause(X, true).
 
:- dynamic(a/1).
a(1). a(2). a(3).

Let’s try to load these two files in SWI-Prolog:

$ swipl
Welcome to SWI-Prolog (Multi-threaded, 32 bits, Version 5.7.7)
Copyright (c) 1990-2008 University of Amsterdam.
SWI-Prolog comes with ABSOLUTELY NO WARRANTY. This is free software,
and you are welcome to redistribute it under certain conditions.
Please visit http://www.swi-prolog.org for details.
 
For help, use ?- help(Topic). or ?- apropos(Word).
 
?- [m1, m2].
% m1 compiled into m1 0.00 sec, 1,316 bytes
ERROR: Cannot import m2:p/1 into module user: already imported from m1
false.
 
[debug]  ?-

We get an error. It seems that loading a module automatically imports its exported predicates into the loading context (the module user in this case). How unfortunate. We don’t get a chance of using explicit qualified calls to choose which predicate to call. Let’s try the same experiment with YAP:

$ yap
% Restoring file /usr/local/lib/Yap/startup
YAP version Yap-5.1.4
   ?- [m1, m2].
 % consulting /Users/pmoura/m1.pl...
 % consulted /Users/pmoura/m1.pl in module m1, 1 msec 1056 bytes
 % consulting /Users/pmoura/m2.pl...
 % consulted /Users/pmoura/m2.pl in module m2, 0 msec 648 bytes
NAME CLASH: m1:p/1 was already imported to module user;
            Do you want to import it from m2 ? [y or n]

In this case, we get a prompt asking what to do. Maybe interesting if we happen to be in an interactive session, otherwise…

Ciao have claimed for a long time to provide a better module system so let’s try a similar experiment with this compiler. This time we explicitly say that we want to use both modules:

$ ciao
Welcome to the Ciao Prolog Development System!
 
Ciao-Prolog 1.10 #8: Wed Jan 31 21:54:06 WET 2007
?- use_module(m1), use_module(m2).
 
yes
?- p(a(X)).
 
X = one ? ;
X = two ? ;
X = three ? ;
no

In this case, there is no error, no prompt, the compiler just silently makes a choice in our behalf. How can the compiler know how to make the right choice? Should the order of the use_module/1 calls really matter? What if there are two exported predicates with the same name in both modules and we want one from the first module and the other from the second module?

What these experiments tell us?

Prolog compilers automatically import, or try to import, the exported predicates of loaded modules. Therefore, whenever two modules export the same predicate, we either get an error (SWI-Prolog), a prompt for solving the conflict (YAP), or automatic conflict resolution (Ciao). So much for the often proclaimed de facto module standard.

Modules are supposed to provide namespaces. Except, it seems, for their exported predicates. In this case, a module developer needs to be aware of all the exported predicates in all modules that might be used by him, or by her, or by third parties in order not to get into trouble. Or risk the chance of getting his or her users into trouble. This is the amazing foundation that many implementers and users alike feel that should be used to build the predicate libraries that will support our Prolog applications. My biggest problem is, of course, how to hide this post from colleagues working in other programming languages. I don’t want them to laugh themselves to death.


One of the obstacles to Prolog success is…

… current module systems. The lack of a strong module standard is often touted as the reason behind the lack of portable Prolog libraries. Problem is, modules fail at everything that we want and need to accomplish when thinking about namespaces in Prolog. Data hiding? No, any module predicate can be called using explicit module qualification. You may think of a module predicate export list as the public module predicates but that’s only wishful thinking. Portability? Only if you forgo the use of operators and meta-predicates and restrict yourself to a subset of the Prolog compilers implementing a module system. The fact that the current ISO Prolog standard for modules is incompatible with the module systems found on most Prolog compilers does not help either. Separation between interface and implementation? No, simply not possible. Conflicts between imported predicates? Compiler errors and warnings will usually tell you there is a problem but modules do not provide mechanisms for solving the conflict other than reordering of import directives (that can only solve some of the problems). Ever wondered why module predicates often use a functor with a prefix which usually is the name of the module? The preferred and recommended way of using modules is through import directives and implicit qualification. Thus, we cannot reuse vocabulary between modules in fear of name conflicts (think polymorphism). Most current Prolog module systems simply do not provide the building blocks needed for the development of portable Prolog libraries. Without portable libraries, users are easily trapped into a single Prolog implementation. Want to shop around for third party libraries? You will be hard-pressed to find even vaporware.

Rats! This post ended up being a rant. I will try to behave next time.