The time line gets difficult. SWI-Prolog (and a few others, such as YAP, Ciao and SICStus) got there module system from Quintus. Wikipedia says Erlang was started in 1986, which is pretty close to when Quintus got modules. So yes, in these module systems a fully qualified predicate indicator is module:name/arity and calls take the form module:name(arg1, arg2, …).
Each module is headed by a
:- module(Name, Exports)., splitting its predicates in exported and private predicates. use_module/1 imports all exported predicates from the target and use_module/2 only the listed ones.
Using module:name(arg1, …) in your code is not what the developers had in mind. You can conclude that from two observations:
- The module:name(arg1, …) construct can call both exported and private predicates. The breaks the most important aspect of modules: distinguish the interface (exported preds) from the private ones, so you can do whatever you want to the implementation as long as you stick to the interface without harming the overall application.
- The module:name(arg1, …) changes the module context to module, i.e., it acts as if the goal is called from inside module. This makes a difference if name(…) is a meta predicate, a predicate calling a goals passed in one or more of the arguments. Think of findall/3, maplist/3, etc. If we call
apply:maplist(mypred, List1, List2) it will call
apply:mypred/2 rather than
mypred/2 in the module from which I make this call.
In other words, qualified goals (module:name(arg1, …)) are intended for debugging and testing, not for production code.
So, how must we deal with this? One way as you suggest is to have fairly small modules (or more precisely, modules with a small interface). This reduces the probability of conflicts when using use_module/1. Another is @peter.ludemann’s suggestion to use use_module/2, possibly with the “… as myname” in case of a conflict (added to SWI-Prolog after discussion with Vitor Santos Costa from YAP).
The third option is the common practice in most of the libraries developed in the Quintus descendant systems. This uses a short prefix for the interface, so you get e.g. ord_disjoint/2, ord_subset/2, etc. Using this type of naming the risk for conflicts when using use_module/2 or even relying on autoloading is pretty small. Note that relying on autoloading gets you pretty much in the same space as C, where you have static (private) functions and common functions that all live in the same namespace (in C the default is common, in Prolog private). The C way to live with that is similar, which is why all SWI-Prolog foreign symbols are called
PL_*, X11 calls everything
Is this bad? I don’t know. The module names live in a flat namespace. This requires using module names that are rather long, typically including some package prefix, e.g.,
http_server, etc. Shorter names can only be used for old common libraries such as
lists. I’ve seen too many packages having a
utils module. You can’t load two of these packages into the same Prolog instance. Well, actually you can as you can use load_files/2 using the module(Name) option to load a module into a different module than the one mentioned in the header. If your code uses
utils:... it still breaks
A more modern solution would be to only allow module:name(…) calls to exported predicates and not switch the calling context. This probably should be combined with a
:- use_module(Path) as Name, such that we can use Name:… to refer to this module regardless of the module name in Path. Logtalk has solved this stuff in a more modern way. Possibly the biggest added value is that it is what most people coming from other recent languages expect.
Migrating to that would require renaming all library modules and their predicates to reach at a coherent system. Is
ord_subtract(All, Subtract, Remain) better than
ordsets:subtract(All, Subtract, Remain)? It gets value if we have different implementations for the same interface. That is hard to do elegantly using this module system.