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:
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.
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), ( foreacharg(Spec,Meta), foreacharg(Arg,Goal), foreacharg(Ann,AGoal), param(M) do ( Spec==(:) -> Ann = M:Arg ; integer(Spec) -> Ann = M:Arg ; Ann = Arg ) ), 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).