# RANT about module system

**URL:** <https://discourse.prolog-lang.org/t/rant-about-module-system/204>\
**Category:** General discussions on Prolog\
**Created:** [September 5, 2026, 2:52pm UTC](https://discourse.prolog-lang.org/t/rant-about-module-system/204 "2026-09-05T14:52:59Z")\
**Posts on this page:** 19\
**Page:** 1

<div class="post-metadata">

**Author:** ![hurufu](https://discourse.prolog-lang.org/user_avatar/discourse.prolog-lang.org/hurufu/32/114_2.png) [@hurufu](https://discourse.prolog-lang.org/u/hurufu)\
**Post date:** [September 5, 2026, 2:52pm UTC](https://discourse.prolog-lang.org/t/rant-about-module-system/204/1 "2026-09-05T14:52:59Z")

</div>

BIG DISCLAIMER: This is a rant, don’t take it too seriously.

tldr: Just add prefix to predicates from same file.

I just hate Quintus-style module system. I’m currently working on a large-ish project and parts of it are written in portable Prolog. And I absolutely don’t and will not use modules. Firstly GNU Prolog doesn’t support them, secondly they introduce more problems then they are solving.

What most programmers need is just predicate visibility (like `static` keyword in C) without stupid `(:)/2` term. I have worked on term expansion and this term introduces too many _unnecessary_ and otherwise avoidable inefficiencies and corner cases to consider so this alone makes it a harmful construct. Even if you don’t see `:` in your code it is silently added by most(?) Prolog engines.

Namespaces is an orthogonal idea to modules. Modules should not introduce new namespace into user program. And user should be able to decide it they want predicates acccessible in any namespace.

Name collision can be simply fixed by adding short mnemonic prefix to you predicates – it actually improves readability quite dramatically.

---

<div class="post-metadata">

**Author:** ![j4n\_bur53](https://discourse.prolog-lang.org/user_avatar/discourse.prolog-lang.org/j4n_bur53/32/62_2.png) [@j4n\_bur53](https://discourse.prolog-lang.org/u/j4n_bur53)\
**Post date:** [September 5, 2026, 3:15pm UTC](https://discourse.prolog-lang.org/t/rant-about-module-system/204/2 "2026-09-05T15:15:18Z")

</div>

> [@hurufu](#):
>
> tldr: Just add prefix to predicates from same file.

It leads to an always quantified style. In practice it happens  
anyways, even if you have modules. Like `http_THIS` or  
`http_THAT`, as done in SWI-Prolog. It also shows that the

prefixing can be topical, not necessarely refering to a single  
module, can also cross modules, and mixed prefixes can be used,  
I am using like `os_THIS`, or `cpu_THAT`, etc…

Do I miss modules? Yes and no. A linter would be cool, for  
enforcement to repeat Prolog text loads, just like people  
repeat C .h file imports (\*), so that Prolog texts become

separate self contained units, loading all they need.  
But there are also patterns of dynamically loading Prolog texts,  
and providing hooks. You find in old Prolog systems text

sources even the use of current\_predicate/1 to see if a  
hook is there or not. The modern way is probably to use  
multifile/1 (\*\*) and if there is no already existing hook, dynamic/1.

A linter could maybe also spot such problems resp solutions.

(\*) Except if you create a master .h file which does all the  
imports for you. But this runs counter to the idea that a  
self contained unit, also means a mimimal import set.

(\*\*) Some of these techniques are even not applicable to GNU Prolog.  
Since GNU Prolog has only include/1 if am not mistaken, and  
multifile/1 is a noop. Its more primitive than ISO core standard.

---

<div class="post-metadata">

**Author:** ![hurufu](https://discourse.prolog-lang.org/user_avatar/discourse.prolog-lang.org/hurufu/32/114_2.png) [@hurufu](https://discourse.prolog-lang.org/u/hurufu)\
**Post date:** [September 5, 2026, 3:30pm UTC](https://discourse.prolog-lang.org/t/rant-about-module-system/204/3 "2026-09-05T15:30:17Z")

</div>

From what I understand Dogelog, GNU (and maybe some others) don’t have modules – and for a good reason. This points me to a question to language implementers: is it really that difficult to add predicate visibility without `(:)/2`? Because limited visibility is good to avoid name clashes, but from what I see it is always resolved using `:`.

---

<div class="post-metadata">

**Author:** ![j4n\_bur53](https://discourse.prolog-lang.org/user_avatar/discourse.prolog-lang.org/j4n_bur53/32/62_2.png) [@j4n\_bur53](https://discourse.prolog-lang.org/u/j4n_bur53)\
**Post date:** [September 5, 2026, 3:34pm UTC](https://discourse.prolog-lang.org/t/rant-about-module-system/204/4 "2026-09-05T15:34:04Z")

</div>

Hi,

> [@hurufu](#):
>
> is it really that difficult to add predicate visibility without `(:)/2`?

The ISO core standard defines ensure\_loaded/1. But  
some module based Prolog systems, like Scryer Prolog  
have for a while refused to implement it. Not sure what

the actual status is. Trealla Prolog might be more tollerant.  
Basically a module system based Prolog might allow  
module less coding for the user space. A Prolog text loaded

by ensure\_loaded/1 need not have a module/1 directive  
header. Module less coding in user space, might give the  
end-user a handle for quick authoring of small scripts,

without the baggage of modules. Many Prolog playgrounds  
offer this already when they show a split screen with some  
Prolog text. Although they might create a “notebook” module

or something for you under the hood. But the idea would be  
to have still file names, and multiple such Prolog texts just like  
defined in the ISO core standard. In SWI-Prolog its quite a

headache, since a module less module, basically attaches to  
a module “user” which is automatically imported everywhere.  
So complexity is quite high, even when the request seems trivial.

Bye

---

<div class="post-metadata">

**Author:** ![JanWielemaker](https://discourse.prolog-lang.org/user_avatar/discourse.prolog-lang.org/janwielemaker/32/24_2.png) [@JanWielemaker](https://discourse.prolog-lang.org/u/JanWielemaker)\
**Post date:** [September 5, 2026, 3:49pm UTC](https://discourse.prolog-lang.org/t/rant-about-module-system/204/5 "2026-09-05T15:49:55Z")

</div>

> [@hurufu](#):
>
> What most programmers need is just predicate visibility (like `static` keyword in C) without stupid `(:)/2` term. I have worked on term expansion and this term introduces too many _unnecessary_ and otherwise avoidable inefficiencies and corner corner cases to consider so this alone makes it a harmful construct. Even if you don’t see `:` in your code it is silently added by most(?) Prolog engines.

The fun thing is that the Quintus module system was designed in the late 80s/early 90s with exactly that in mind: provide local predicates and have zero overhead. Just meta predicates that are exported from a module need a meta\_predicate declaration to get the calling context right. foo:p(\_) should have zero overhead. Using imported meta-predicates come with.a small overhead, but generally tiny compared to the meta call that is typically involved anyway.

For short, take.a non-module program. Add a module header listing all predicates that are called from other modules. Add meta\_predicate declarations for all exported meta predicates and import the modules you need and you have a working program that is just as fast as what you started with.

The obvious gain: it is immediately clear what is public and what is private in a module and what modules depend on what other modules. Using a good editor that highlights the import/export status of predicates and calls completes the picture.

---

<div class="post-metadata">

**Author:** ![hurufu](https://discourse.prolog-lang.org/user_avatar/discourse.prolog-lang.org/hurufu/32/114_2.png) [@hurufu](https://discourse.prolog-lang.org/u/hurufu)\
**Post date:** [September 5, 2026, 4:09pm UTC](https://discourse.prolog-lang.org/t/rant-about-module-system/204/6 "2026-09-05T16:09:40Z")

</div>

> [@JanWielemaker](#):
>
> Just meta predicates that are exported from a module need a meta\_predicate declaration to get the calling context right.

It still sounds too complex solution for a simple zero-cost visibility problem. I never implemented Prolog (but I tried to participate briefly) and I would’ve done this by name mangling.

So all “private” predicates have automatic prefix derived from the file names. And meta-predicates will still work because this prefix is known in the current file. So if you do let’s say `maplist(small_pred_, MyList)` then during compilation `small_pred_` is replaced with `myfile_small_pred_` and that’s all. No `(:)/2` is needed!

---

<div class="post-metadata">

**Author:** ![hurufu](https://discourse.prolog-lang.org/user_avatar/discourse.prolog-lang.org/hurufu/32/114_2.png) [@hurufu](https://discourse.prolog-lang.org/u/hurufu)\
**Post date:** [September 5, 2026, 4:19pm UTC](https://discourse.prolog-lang.org/t/rant-about-module-system/204/7 "2026-09-05T16:19:14Z")

</div>

My main source of frustration is having to deal with both `G_0` and `M:G_0` variables holding callable terms if I want to do anything advanced/generic. And those terms have very different structure, for example `functor/3` will give unexpected results, etc.

---

<div class="post-metadata">

**Author:** ![j4n\_bur53](https://discourse.prolog-lang.org/user_avatar/discourse.prolog-lang.org/j4n_bur53/32/62_2.png) [@j4n\_bur53](https://discourse.prolog-lang.org/u/j4n_bur53)\
**Post date:** [September 5, 2026, 11:47pm UTC](https://discourse.prolog-lang.org/t/rant-about-module-system/204/8 "2026-09-05T23:47:51Z")

</div>

Hi,

In a module less prolog system, you indeed need to do nothing.  
There are no `(:)/2` terms that are passed as meta arguments, or  
appear elsewhere for some reason.

So in a module less system you only have `f(t1,..,tn)` and functor/3  
gives you `f` and `n`. In Prolog system with modules you might also  
find a term of the form `m:f(t1,..,tn)`. So people are using this

predicate as well, before calling functor/3 I guess, or after  
calling functor/3, depending on the mode of your usage:

> **strip\_module(+Term, -Module, -Plain)**  
> [https://www.swi-prolog.org/pldoc/man?predicate=strip\_module/3](https://www.swi-prolog.org/pldoc/man?predicate=strip_module/3)

In a module less Prolog system, calling strip\_module/3, likely  
gives a non-existence error. But you could define a stub:

```auto
strip_module(T, user, T).

```

Unfortunately `(:)/2` has a couple of more use cases than module  
quantification. For example you can use it for dicts in some systems:

```auto
{x:120, y:75}

```

So most algorithm indeed need some meta information about,  
what is a term, allowing dicts mostlikely, and what is a goal.

Bye

---

<div class="post-metadata">

**Author:** ![hurufu](https://discourse.prolog-lang.org/user_avatar/discourse.prolog-lang.org/hurufu/32/114_2.png) [@hurufu](https://discourse.prolog-lang.org/u/hurufu)\
**Post date:** [September 6, 2026, 5:34am UTC](https://discourse.prolog-lang.org/t/rant-about-module-system/204/9 "2026-09-06T05:34:28Z")

</div>

> [@j4n\_bur53](#):
>
> In a module less prolog system, you indeed need to do nothing.  
> There are no `(:)/2` terms that are passed as meta arguments, or  
> appear elsewhere for some reason.

Maybe in some systems that’s true, idk, but here there is explicit check for `:` [Goal expansion in lib reif by hurufu · Pull Request #2433 · mthom/scryer-prolog · GitHub](https://github.com/mthom/scryer-prolog/pull/2433/changes#diff-66e6602c16fe2c6b5e3a1ec479c7028400815893300080c9f500427fb11bbefaR87)

Maybe that’s not the best example, but I saw a lot of code that had to deal explicitly with terms with `:` as a primary functor.

---

<div class="post-metadata">

**Author:** ![j4n\_bur53](https://discourse.prolog-lang.org/user_avatar/discourse.prolog-lang.org/j4n_bur53/32/62_2.png) [@j4n\_bur53](https://discourse.prolog-lang.org/u/j4n_bur53)\
**Post date:** [September 6, 2026, 10:16am UTC](https://discourse.prolog-lang.org/t/rant-about-module-system/204/10 "2026-09-06T10:16:28Z")

</div>

Hi,

> [@hurufu](#):
>
> Maybe that’s not the best example, but I saw a lot of code that  
> had to deal explicitly with terms with `:` as a primary functor.

The `library(reif)` seems to have been written with a Prolog  
system in mind, that has modules. The `(:)/2` operator can  
be added by a Prolog system to meta arguments so that

caller meta argument calls, know where to lookup predicates.  
So in cases like for example such as below, you even don’t need to  
program it via `call/1` or any `(:)/2`, it stands without, because of

some peculiarities of the ISO core standard and how it defines `(\+)/1`,  
and how the module standard simply extends the definition of  
preparing a goal for execution (there is the quivalent of preparing

a clause for assertion in the ISO core standard):

```auto
forall(A, B) :- 
   \+ (A, \+ B).

```

You don’t see that at runtime `(:)/2` terms are passed around.  
And calls to forall/2 are expanded by the current module context.  
But then there are 1-2 more use cases where `(:)/2` appears.

They are, inside the body of a clause as goals:

```auto
/* constant module call */
foo:bar

/* variable module call */
M=foo, M:bar

```

So the end-user is free to use `(:)/2` for such calls in his code  
as well, when he wants to specify a module in his code by either  
a constant or a variable. There is a module standard which

explains most of it:

> **ISO/IEC 13211-2:2000(en) - Part 2: Modules**  
> [https://www.iso.org/obp/ui#iso:std:iso-iec:13211:-2:ed-1:v1:en](https://www.iso.org/obp/ui#iso:std:iso-iec:13211:-2:ed-1:v1:en)

Bye

P.S. But the module standard is relatively weak, it does not contain  
programming utilities like `strip_module/3`. And also people, as  
the library(reif) example shows, might not use strip\_module/3.

The module standard has only these new and modified built-ins,  
which is not 100% of the module story. And might even not completely  
be followed by all Prolog systems that have modules:

 ![image](https://discourse.prolog-lang.org/uploads/default/original/1X/d4bd7f3a25634df703c9e4384a8a479cb42be3f5.png)

---

<div class="post-metadata">

**Author:** ![JanWielemaker](https://discourse.prolog-lang.org/user_avatar/discourse.prolog-lang.org/janwielemaker/32/24_2.png) [@JanWielemaker](https://discourse.prolog-lang.org/u/JanWielemaker)\
**Post date:** [September 6, 2026, 11:32am UTC](https://discourse.prolog-lang.org/t/rant-about-module-system/204/12 "2026-09-06T11:32:56Z")

</div>

> [@hurufu](#):
>
> It still sounds too complex solution for a simple zero-cost visibility problem. I never implemented Prolog (but I tried to participate briefly) and I would’ve done this by name mangling.

That is roughly what XSB does for compound terms (f(…)). It comes with advantages and disadvantages. I find it a bit hard to judge which is overall better. The problem is that compound terms have two roles: they represent goals, but also data. XSB atoms are global, which implies they need a kludge for zero-arity predicates that are internally more like p() :- …

But, both approaches have pretty much zero runtime overhead and both requires some care when converting.a non-modular program to a modular one. For Quintus, these are exported meta predicates, for XSB that concerns data visibility and maybe zero-arity predicates. I might be wrong on the XSB details. XSB people are on this list, so please correct. The rationale behind Quintus is that exported meta-predicates are a quite advanced topic and users managing that are likely capable of dealing with the complexity.

---

<div class="post-metadata">

**Author:** ![j4n\_bur53](https://discourse.prolog-lang.org/user_avatar/discourse.prolog-lang.org/j4n_bur53/32/62_2.png) [@j4n\_bur53](https://discourse.prolog-lang.org/u/j4n_bur53)\
**Post date:** [September 6, 2026, 6:04pm UTC](https://discourse.prolog-lang.org/t/rant-about-module-system/204/15 "2026-09-06T18:04:54Z")

</div>

Hi,

> [@JanWielemaker](#):
>
> That is roughly what XSB does for compound terms (f(…)).  
> It comes with advantages and disadvantages. I find it a bit  
> hard to judge which is overall better.

If client code would use [strip\_module/3](https://www.swi-prolog.org/pldoc/man?predicate=strip_module/3) consistently. It wouldn’t  
matter how qualified calls are encoded. You can make it completely  
opaque. But you might mostlikely still see `(:)/2` in read term.

It might not be used in practice for Prolog systems and Prolog client  
code, when the code wants to stay free of such low level helper tricks,  
like leaking some optimization data structures into user space.

Bye

P.S.: You can also do such internalization of module less calls, for  
speed up. Its very much the same principle like internalizing strings  
for example. Only it might have a different goal than deduplication.

I use `ir_site_new/2` to create internal representations of closures:

```auto
maplist(C, L) :-
   ir_site_new(C, D),
   sys_maplist(L, D).

sys_maplist([], _).
sys_maplist([X|L], C) :-
   call(C, X),
   sys_maplist(L, C).

```

The goal of the internalization? A faster repeated predicate lookup!

P.S.: It is based on a hint in some of the writings by Richard O’Keefe.

---

<div class="post-metadata">

**Author:** ![David\_Warren](https://discourse.prolog-lang.org/user_avatar/discourse.prolog-lang.org/david_warren/32/23_2.png) [@David\_Warren](https://discourse.prolog-lang.org/u/David_Warren)\
**Post date:** [September 6, 2026, 7:05pm UTC](https://discourse.prolog-lang.org/t/rant-about-module-system/204/16 "2026-09-06T19:05:08Z")

</div>

Regarding the XSB module system: A functor symbol always has a particular module associated with it. Code is associated with a functor symbol and, when predicate is called, the code associated with that symbol (in its module) is executed. There is zero overhead for invoking a predicate in a module; the exact same code address look-up is done.  
How does a predicate symbol get its module? A Prolog source file may define a module (if the code includes an export declaration), in which case its predicate symbols are considered to be in that module. If a source file doesn’t define a module, then all its predicates are assumed to be in a standard “global” module, called “usermod”. Any code file can contain an :- import P/A from Mod. command, in which case then all uses of the predicate P/A will be assumed to have Mod as their associated module.

That is the basic XSB module system. Then (perhaps against my better judgment:-), we added a syntax for call of, e.g., Mod:p(…). If Mod is a constant, then this is compiled exactly the same as if you had declared p/n as imported from Mod. If Mod is a variable, then code is called to find the predicate symbol with the indicated name in the indicated module and call it. It this case, there is some overhead in the execution of the call. In all other cases, there is zero runtime overhead.  
As Jan correctly noted, there are complexities/anomalies with 0-ary predicates, since in XSB atoms don’t have associated modules. Also, you can’t use moda:p(X) and modb:p(X) in the same module because that would lead to an attempt to import the same predicate from two different modules. (You can do it explicitly with :- import p/1 from modB as p\_from\_modB/1 and using p\_from\_modB(X), but I agree that might be awkward. And to be completely pedantic, executing the call to p\_from\_modB has an extra overhead of a single WAM jump instruction)

As Jan says, there are pros and cons to both XSB’s functor-based module system and Quintus’s predicate-based one. I’m partial to XSB’s, but maybe that’s not a surprise:-).

-David

---

<div class="post-metadata">

**Author:** ![j4n\_bur53](https://discourse.prolog-lang.org/user_avatar/discourse.prolog-lang.org/j4n_bur53/32/62_2.png) [@j4n\_bur53](https://discourse.prolog-lang.org/u/j4n_bur53)\
**Post date:** [September 7, 2026, 6:54am UTC](https://discourse.prolog-lang.org/t/rant-about-module-system/204/17 "2026-09-07T06:54:25Z")

</div>

> [@David\_Warren](#):
>
> As Jan says, there are pros and cons to both XSB’s **functor-based**  
> module system and Quintus’s predicate-based one. I’m partial to XSB’s,  
> but maybe that’s not a surprise:-).

So the Wikipedia entry about XSB has an error, due to the 50 years  
of Prolog paper having an error, wrongly attributing the topic  
to the “XSB as an efficient Deductive Database Engine” paper:

> /\* false claim by an encyclopedia \*/  
> XSB uses a module system based on Prolog atoms.  
> [https://en.wikipedia.org/wiki/XSB](https://en.wikipedia.org/wiki/XSB)

If I interpret your explanations correctly atoms have a global scope.  
But functors of arity \> 0 have a local scope, and can be imported by  
other modules. And you call it yourself a functor-based module

system, and not an atom-based module system. Calling it atom-based  
seems to miss the mark, that functors are imported based on their arity.  
In as far XSB has not introduced means like strip\_module/3 in their

module system, but rather functor/4 to walk its data structures:

> **functor(?Term, ?Module, ?Functor, ?Arity)**  
> The predicate functor/4 extends the functionality of functor/3 to  
> include the handling of modules. In XSB’s module system (See 3.4),  
> every structured term is in a module.

[https://xsb.sourceforge.net/](https://xsb.sourceforge.net/)

But XSB has also a declarations such as `:- hilog closure` which  
then forces the functor to be an atom via an apply/n wrapper. I guess  
taking advantage of the then global scope for higher order calling.

So the maplist/2 example I gave would work out a little differently.  
Although XSB might even not have maplist/2 at all, these are things  
that entered Prolog systems from functional programming recently.

---

<div class="post-metadata">

**Author:** ![David\_Warren](https://discourse.prolog-lang.org/user_avatar/discourse.prolog-lang.org/david_warren/32/23_2.png) [@David\_Warren](https://discourse.prolog-lang.org/u/David_Warren)\
**Post date:** [September 7, 2026, 3:47pm UTC](https://discourse.prolog-lang.org/t/rant-about-module-system/204/18 "2026-09-07T15:47:55Z")

</div>

Yes, there is that confusion with notion of “atom”. That term has a checkered history in logic. I think it is better described as a “term” based module system. My impression is that the phrase “atom-based” as applied to a module system started with BIM-Prolog. Gemini seems to agree, responding to my question about the kind of module system BIM had with “BIM-Prolog utilized an **atom-based** module system rather than a predicate-based one.” Anyway…

I’m not sure what strip\_module does, but it sounds like it makes the predicate not be in a module. That’s not possible in XSB; every functor symbol has a module. You could change it to usermod, if you wished, but you’re stuck with some module. And, yes, meta-programming is different in the different module frameworks. I find it usually more convenient with the XSB module system, but again that is a matter of taste and familiarity.

Hilog, in XSB, is very different beast. When that syntax was added to XSB, it was NOT integrated with the module system, at least not very well. And that was a serious mistake. For it to be more useful, it should be changed to integrate batter. I, personally, am not convinced it is really worth the effort and incompatibilities at this point, but I could be convinced.

-David

---

<div class="post-metadata">

**Author:** ![JanWielemaker](https://discourse.prolog-lang.org/user_avatar/discourse.prolog-lang.org/janwielemaker/32/24_2.png) [@JanWielemaker](https://discourse.prolog-lang.org/u/JanWielemaker)\
**Post date:** [September 7, 2026, 4:05pm UTC](https://discourse.prolog-lang.org/t/rant-about-module-system/204/19 "2026-09-07T16:05:00Z")

</div>

> [@David\_Warren](#):
>
> I’m not sure what strip\_module does

It is a SWI-Prolog addition. It takes a term, embedded in zero or more `Module:RealTerm` and gives you the innermost module (the most deeply nested `Module:RealTerm` or the current _context_ module) and the plain term. It is not used a lot, especially since a predicate that defines an argument as a meta-argument is guaranteed to receive a plain `Module:RealTerm` term, i.e., the :/2 term is always there and it is never nested.

---

<div class="post-metadata">

**Author:** ![j4n\_bur53](https://discourse.prolog-lang.org/user_avatar/discourse.prolog-lang.org/j4n_bur53/32/62_2.png) [@j4n\_bur53](https://discourse.prolog-lang.org/u/j4n_bur53)\
**Post date:** [September 9, 2026, 7:47am UTC](https://discourse.prolog-lang.org/t/rant-about-module-system/204/22 "2026-09-09T07:47:53Z")

</div>

Hi,

> [@David\_Warren](#):
>
> Hilog, in XSB, is very different beast. When that syntax was added to XSB, it was NOT integrated with the module system, at least not very well. And that was a serious mistake. For it to be more useful, it should be changed to integrate batter. I, personally, am not convinced it is really worth the effort and incompatibilities at this point, but I could be convinced

Well lean and mean is totally en vogue. Artificial Intelligence (AI)  
companies currently making mathematicians obsolete by fleshing out,  
in increasing frequency, Microsoft Lean theorem prover based  
certificates, for so far unresolved conjectures.

So the ISO core standard has just bypassed, spending 10  
years in a philosophy faculty and marvelling how [homoiconicity](https://en.wikipedia.org/wiki/Homoiconicity)  
could be modelled inside set theory and FOL, by making  
call/n part of the aforementioned standard:

> **8.15.4 call/2..8**  
> These built-in predicates provide support for higher-order programming.  
> a) Let `call(p(X`1`,...,X`M`), Y`2`, ..., Y`N`)` be the goal to be executed, `M` ≥ 0,  
> b) Execute `call(p(X`1`, ..., X`M`, Y`2`, ..., Y`N`))` instead.  
> [https://www.complang.tuwien.ac.at/ulrich/iso-prolog/dtc2#call](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/dtc2#call)

I used call/2 in the maplist example. But more strikingly are  
examples where higher-order logic programming gives alternative  
solutions to object orientation and/or parameterized modules.  
Take this example from SICStus Prolog:

```prolog
rat :: {
        (P/Q < R/S) :- :(P*S < Q*R)
        }.

sort(Type) :: {
        :- :use_module(library(lists), [append/3]) &

        qsort([], []) &
        qsort([P|L], S) :-
                partition(L, P, Small, Large),
                qsort(Small, S0),
                qsort(Large, S1),
                :append(S0, [P|S1], S) &

        partition([], _P, [], []) &
        partition([X|L1], P, Small, Large) :-
                ( Type :: (X < P) ->
                    Small = [X|Small1], Large = Large1
                ; Small = Small1, Large = [X|Large1]
                ),
                partition(L1, P, Small1, Large1)
        }.

```

And then an example query:

```prolog
?- sort(rat) :: qsort([23/3, 34/11, 45/17], L).
L = [45/17,34/11,23/3]

```

[https://sicstus.sics.se/sicstus/docs/3.12.3/html/sicstus/Generic-Objects-for-Easy-Reuse.html](https://sicstus.sics.se/sicstus/docs/3.12.3/html/sicstus/Generic-Objects-for-Easy-Reuse.html)

Using higher order and the core only, is a little clumsy,  
it requires more argument passing, but an implementation  
of call/n as a built-in is trivial and doesn’t require any new  
syntax operators. It gives the same results:

```prolog
:- ensure_loaded(library(hiord)). %%% call/n built-in

rat_less(P/Q, R/S) :-
    P*S < Q*R.

qsort([], [], _).
qsort([P|L], S, C) :-
    partition(L, P, Small, Large, C),
    qsort(Small, S0, C),
    qsort(Large, S1, C),
    append(S0, [P|S1], S).

partition([], _, [], [], _).
partition([X|L1], P, Small, Large, C) :-
    ( call(C, X, P) ->
        Small = [X|Small1], Large = Large1
    ; Small = Small1, Large = [X|Large1]
    ),
    partition(L1, P, Small1, Large1, C).

```

And then an example query:

```prolog
?- qsort([23/3, 34/11, 45/17], L, rat_less).
L = [45/17, 34/11, 23/3].

```

The result is obtained with smaller footprint in Prolog system  
size and complexity. So while object orientation and/or parameterized  
modules seem to be disposable. Higher-order logic programming  
with call/n gives an almost indisposable spin to homoiconicity.

Bye

---

<div class="post-metadata">

**Author:** ![hurufu](https://discourse.prolog-lang.org/user_avatar/discourse.prolog-lang.org/hurufu/32/114_2.png) [@hurufu](https://discourse.prolog-lang.org/u/hurufu)\
**Post date:** [September 9, 2026, 8:20am UTC](https://discourse.prolog-lang.org/t/rant-about-module-system/204/23 "2026-09-09T08:20:26Z")

</div>

Digression:

> [@j4n\_bur53](#):
>
> And then an example query:
> 
> ```auto
> ?- qsort([23/3, 34/11, 45/17], L, rat_less).
> L = [45/17, 34/11, 23/3].
> 
> ```

This is surprisingly similar to modules in linear logic programming: [Lolli: A Linear Logic Programming Language](https://www.lix.polytechnique.fr/~dale/lolli/)

---

<div class="post-metadata">

**Author:** ![j4n\_bur53](https://discourse.prolog-lang.org/user_avatar/discourse.prolog-lang.org/j4n_bur53/32/62_2.png) [@j4n\_bur53](https://discourse.prolog-lang.org/u/j4n_bur53)\
**Post date:** [September 9, 2026, 8:26am UTC](https://discourse.prolog-lang.org/t/rant-about-module-system/204/24 "2026-09-09T08:26:10Z")

</div>

Hi,

> [@hurufu](#):
>
> This is surprisingly similar to modules in linear logic programming

Not really. Its based on embedded implication, which is  
a different mechanism. It is this inference rule which is not  
in the ISO core standard, and cannot easily be added  
by a built-in like call/n. The inference rule is supposition:

![impl_proof](https://discourse.prolog-lang.org/uploads/default/original/1X/421218dc89b7884bb5f3d51f4e28a49c4a592d22.gif)

One might try to hide the details when and how supposition  
is invoked when you use your modules. But concerning  
the footprint in Prolog system size and complexity, you  
have to consider the trade off between supposition based

translations and a tiny call/n built-in predicate. With supposition  
the query would be basically as below. So you don’t have an  
additional parameter, but you posit dynamically and temporarily  
a program, supposition is the mechanism for reuse:

```auto
?- {less(P/Q, R/S) :- P*S < Q*R} => qsort([23/3, 34/11, 45/17], L).
L = [45/17, 34/11, 23/3].

```

And (=\>)/2 might be slow, if your supposition tries to provide large  
Prolog terms in a term sharing manner, it might copy them! So  
your embedded implication would need to solve a lot of issues.  
On the other hand a predicate call/n usually automatically plays

well with the structure sharing of modern Prolog systems.

Bye

P.S.: A little slower than a built-in call/n is this bootstrapping:

```auto
call(C, X) :- 
    ...

call(C, X, Y) :-
    C =.. [F|L],
    append(L, [X,Y], R),
    D =.. [F|R],
    call(D).

call(C, X, Y, Z) :- 
    ...

```

If you have a module system as well some new challenges might enter,  
since atoms, the F, are now polymorphic in arity. So there is an additional  
semantic complication involved, besides juggling with strip\_module/3.
