Module expansion of goals passed to library meta-predicates
Asked Answered
I

1

5

Using SWI-Prolog (Multi-threaded, 64 bits, Version 7.3.5), we proceed step by step:

  1. Define nonterminal a//1 in module dcgAux (pronounced: "di-SEE-goh"):

    :- module(dcgAux,[a//1]).
    
    a(0)    --> [].
    a(s(N)) --> [a], a(N).
    
  2. Run the following queries—using phrase/2 and apply:foldl/4:

    ?- use_module([library(apply),dcgAux]).
    true.
    
    ?- phrase(      foldl(       a,[s(0),s(s(0))]),[a,a,a]).
    true.
    
    ?- phrase(      foldl(dcgAux:a,[s(0),s(s(0))]),[a,a,a]).
    true.
    
    ?- phrase(apply:foldl(dcgAux:a,[s(0),s(s(0))]),[a,a,a]).
    true.
    
    ?- phrase(apply:foldl(       a,[s(0),s(s(0))]),[a,a,a]).
    ERROR: apply:foldl_/4: Undefined procedure: apply:a/3
    

    нет! Quite a surprise—and not a good one. Have we been missing some unknown unknowns?

  3. To get rid of above irritating behavior, we must first find out the reason(s) causing it:

    ?- import_module(apply,M), M=user.
    false.
    
    ?- phrase(apply:foldl(a,[s(0),s(s(0))]),[a,a,a]).
    ERROR: apply:foldl_/4: Undefined procedure: apply:a/3
    
    ?- add_import_module(apply,user,end).
    true.
    
    ?- import_module(apply,M), M=user.   % sic!
    M = user.                            % `?- import_module(apply,user).` fails!
    
    ?- phrase(apply:foldl(a,[s(0),s(s(0))]),[a,a,a]).
    true.
    

What's going on? The way I see it is this:

  • Module expansion of the goal passed to foldl/4 is limited.
  • Quoting from the SWI-Prolog manual page on import_module/2:

    All normal modules only import from user, which imports from system.

  • SWI's library(apply) only "inherits" from system, but not user.

  • If we clone module apply to applY (and propagate the new module name), we observe:

    ?- use_module(applY).
    true.
    
    ?- phrase(applY:foldl(a,[s(0),s(s(0))]),[a,a,a]).  % was: ERROR
    true.                                              % now: OK!
    

Please share your ideas on how I could/should proceed!

(I have not yet run a similar experiment with other Prolog processors.)

Insupportable answered 21/8, 2015 at 13:10 Comment(3)
@false. How clumsy! I was misled by the sloppy (too colloquial) uses of "standard" by others, most notably caml.inria.fr/pub/docs/manual-ocaml/stdlib.html . Thanks for noticing! Should be better now!Insupportable
@false. In general, English is notoriously prone to ambiguity ( just look at dict.cc/?s=standardo ). I have seen theoretical math guys use "standard" / "non-standard" in peculiar ways. And, what about the word "normal"?Insupportable
@false. "Vendor-supplied" is not the right phrase. I get that, but I'd rather use a better phrase than none. Why? With SWI, library(apply) and applY behave differently if add_import_module/3 is not used.Insupportable
R
7

This is an inherent feature/bug of predicate based module systems in the Quintus tradition. That is, this module system was first developed for Quintus Prolog. It was adopted subsequently by SICStus (after 0.71), then (more or less) by 13211-2, then by YAP, and (with some modifications) by SWI.

The problem here is what exactly an explicit qualification means. As long as the goal is no meta-predicate, things are trivially resolvable: Take the module of the innermost qualification. However, once you have meta-predicates, the meta-arguments need to be informed of that module ; or not. If the meta-arguments are informed, we say that the colon sets the calling context, if not, then some other means is needed for that purpose.

In Quintus tradition, the meta-arguments are taken into account. With the result you see. As a consequence you cannot compare two implementations of the same meta-predicate in the same module directly. There are other approaches most notably IF and ECLiPSe that do not change the calling context via the colon. This has advantages and disadvantages. The best is to compare them case by case.

Here is a recent case. Take lambdas and how they are put into a module in SICStus, in SWI, and in ECLiPSe.

As for the Quintus/SICStus/YAP/SWI module system, I'd rather use it in the most conservative manner possible. That is:

  • no explicit qualification, consider the infix : as something internal

  • clean, checkable meta-declarations - insert on purpose an undefined predicate just to see whether or not cross-referencing is able to detect the problem (in SWI that's check or make).

  • use the common subset, avoid the many bells and whistles - there are many well meant extensions...

  • do more versatile things the pedestrian way: reexport by adding an appropriate module and add a dummy definition. Similarly, instead of renaming, import things from an interface module.

  • be always aware that module systems have inherently some limits. No matter how you twist or turn it. There is no completely seamless module system, for the very purpose of modules is to separate code and concerns.


1: To be precise, SICStus' adaptation of Quintus modules did only include : for module sensitive arguments in meta_predicate declarations. The integers 0..9, which are so important for higher-order programming based on call/N were only introduced about 20 years later in 4.2.0 released 2011-03-08.

Rosser answered 21/8, 2015 at 16:3 Comment(3)
Up to now my experience with Prolog module system(s) has been modest at best. Very, that is. Your answer rings true. I appreciate pieces of advise and/or hints on "best practices"!Insupportable
I was on the fence and weighing pros and cons of explicit quantification, particularly for (small) code snippets---not so much for bigger projects (which ideally start with a consensus on arch/design/layout/etc). How about that ?- [user]. somecode.^D in combination with explicit qualification like user:a?Insupportable
Here is a problem with functor based modules.Rosser

© 2022 - 2024 — McMap. All rights reserved.