4.11.17 Semantics of Module Name Expansion

Although module name expansion is performed when code is consulted, compiled or asserted, it is perhaps best explained in terms of an interpreter, especially the issue of how deeply clauses are expanded. The semantics of call/1, taking meta_predicate declarations into account, is shown as if defined by the interpreter shown below. The interpreter's case analysis is as follows:

control constructs
(Including cuts and module prefixes). The interpreter implements the semantics of the construct, expanding its argument.
callable terms with functor N/A
First, we look for a meta_predicate declaration for N/A. If one exists, the relevant arguments are expanded. Otherwise, the goal is left unexpanded. Then, if N/A is a built-in predicate, it is called. Otherwise, a clause with head functor N/A is looked up using the imaginary predicate :-/2, unified against, and its body is interpreted.
non-callable terms
Raise error exception.

Throughout the interpretation, we must keep track of the module context. The interpreter is as follows, slightly simplified. -->/2 is not a predicate:

     call(M:Body) :-
             call(Body, M).
     
     call(Var, M) :- \+callable(Var), !,
             must_be(Term, callable, call(M:Var), 1).
     call(!, _) :- !,
             % cut relevant choicepoints.
     call((A, B), M) :- !,
             call(A, M),
             call(B, M).
     call((A -> B), M) :- !,
         (   call(A, M) ->
             call(B, M)
         ).
     call((A -> B ; C), M) :- !,
         (   call(A, M) ->
             call(B, M)
         ;   call(C, M)
         ).
     call((A ; B), M) :- !,
         (   call(A, M)
         ;   call(B, M)
         ).
     call(\+(A), M) :- !,
         (   call(A, M) ->
             fail
         ;   true
         ).
     call(_^A, M) :- !,
             call(A, M).
     call(do(Iter,Body), M) :- !,
         (   Iter
         do  call(Body, M)
         ).
     call(if(A,B,C), M) :- !,
          if(call(A, M),
             call(B, M),
             call(C, M)).
     call(once(A), M) :- !,
         (   call(A, M) -> true
         ).
     call(Goal, M) :-
         (   predicate_property(M:Goal, meta_predicate(Meta)) ->
             functor(Goal, Name, Arity),
             functor(AGoal, Name, Arity),
             annotate_goal(0, Arity, Meta, Goal, AGoal, M),
             call_goal(AGoal, M)
         ;   call_goal(Goal, M)
         ).
     
     call_goal(asserta(X), _) :- !,
             asserta(X).
     call_goal(asserta(X,R), _) :- !,
             asserta(X, R).
     % and so on for all built-in predicates
     call_goal(Goal, M) :-
             (M:Goal :- Body),
             call(Body, M).
     
     annotate_goal(A, A, _, _, _, _) :- !.
     annotate_goal(I, A, Meta, Goal, AGoal, Module) :-
             J is I+1,
             arg(J, Meta, M),
             arg(J, Goal, Arg),
             arg(J, AGoal, Ann),
             (   M==(:) -> Ann = Module:Arg
             ;   Ann = Arg
             ),
             annotate_goal(J, A, Meta, Goal, AGoal, Module).

Send feedback on this subject.