4.9 Meta-Call Predicates

Meta-call predicates are used to call terms constructed at run time. The basic meta-call mechanism offered by SWI-Prolog is to use variables as a subclause (which should of course be bound to a valid goal at runtime). A meta-call is slower than a normal call as it involves actually searching the database at runtime for the predicate, while for normal calls this search is done at compile time.

[ISO]call(:Goal)
Invoke Goal as a goal. Note that clauses may have variables as subclauses, which is identical to call/1.
[ISO]call(:Goal, +ExtraArg1, ...)
Append ExtraArg1, ExtraArg2, ... to the argument list of Goal and call the result. For example, call(plus(1), 2, X) will call plus(1, 2, X), binding X to 3.

The call/[2..] construct is handled by the compiler. The predicates call/[2-8] are defined as real (meta-)predicates and are available to inspection through current_predicate/1, predicate_property/2, etc.45Arities 2..8 are demanded by ISO/IEC 13211-1:1995/Cor.2:2012. Higher arities are handled by the compiler and runtime system, but the predicates are not accessible for inspection.46Future versions of the reflective predicate may fake the presence of call/9.. . Full logical behaviour, generating all these pseudo predicates, is probably undesirable and will become impossible if max_arity is removed.

apply(:Goal, +List)
Append the members of List to the arguments of Goal and call the resulting term. For example: apply(plus(1), [2, X]) calls plus(1, 2, X). New code should use call/[2..] if the length of List is fixed.
not(:Goal)
True if Goal cannot be proven. Retained for compatibility only. New code should use \+/1.
[ISO]once(:Goal)
Defined as:
once(Goal) :-
        Goal, !.

once/1 can in many cases be replaced with ->/2. The only difference is how the cut behaves (see !/0). The following two clauses are identical:

1) a :- once((b, c)), d.
2) a :- b, c -> d.
ignore(:Goal)
Calls Goal as once/1, but succeeds, regardless of whether Goal succeeded or not. Defined as:
ignore(Goal) :-
        Goal, !.
ignore(_).
call_with_depth_limit(:Goal, +Limit, -Result)
If Goal can be proven without recursion deeper than Limit levels, call_with_depth_limit/3 succeeds, binding Result to the deepest recursion level used during the proof. Otherwise, Result is unified with depth_limit_exceeded if the limit was exceeded during the proof, or the entire predicate fails if Goal fails without exceeding Limit.

The depth limit is guarded by the internal machinery. This may differ from the depth computed based on a theoretical model. For example, true/0 is translated into an inline virtual machine instruction. Also, repeat/0 is not implemented as below, but as a non-deterministic foreign predicate.

repeat.
repeat :-
        repeat.

As a result, call_with_depth_limit/3 may still loop infinitely on programs that should theoretically finish in finite time. This problem can be cured by using Prolog equivalents to such built-in predicates.

This predicate may be used for theorem provers to realise techniques like iterative deepening. It was implemented after discussion with Steve Moyle smoyle@ermine.ox.ac.uk.

setup_call_cleanup(:Setup, :Goal, :Cleanup)
Calls (once(Setup), Goal). If Setup succeeds, Cleanup will be called exactly once after Goal is finished: either on failure, deterministic success, commit, or an exception. The execution of Setup is protected from asynchronous interrupts like call_with_time_limit/2 (package clib) or thread_signal/2. In most uses, Setup will perform temporary side-effects required by Goal that are finally undone by Cleanup.

Success or failure of Cleanup is ignored, and choice-points it created are destroyed (as once/1). If Cleanup throws an exception, this is executed as normal.bugDuring the execution of Cleanup, garbage collection and stack-shifts are disabled.

Typically, this predicate is used to cleanup permanent data storage required to execute Goal, close file descriptors, etc. The example below provides a non-deterministic search for a term in a file, closing the stream as needed.

term_in_file(Term, File) :-
        setup_call_cleanup(open(File, read, In),
                           term_in_stream(Term, In),
                           close(In) ).

term_in_stream(Term, In) :-
        repeat,
        read(In, T),
        (   T == end_of_file
        ->  !, fail
        ;   T = Term
        ).

Note that it is impossible to implement this predicate in Prolog. The closest approximation would be to read all terms into a list, close the file and call member/2. Without setup_call_cleanup/3 there is no way to gain control if the choice-point left by repeat/0 is removed by a cut or an exception.

setup_call_cleanup/3 can also be used to test determinism of a goal, providing a portable alternative to deterministic/1:

?- setup_call_cleanup(true,(X=1;X=2), Det=yes).

X = 1 ;

X = 2,
Det = yes ;

This predicate is under consideration for inclusion into the ISO standard. For compatibility with other Prolog implementations see call_cleanup/2.

setup_call_catcher_cleanup(:Setup, :Goal, +Catcher, :Cleanup)
Similar to setup_call_cleanup(Setup, Goal, Cleanup) with additional information on the reason for calling Cleanup. Prior to calling Cleanup, Catcher unifies with the termination code (see below). If this unification fails, Cleanup is not called.
exit
Goal succeeded without leaving any choice-points.
fail
Goal failed.
!
Goal succeeded with choice-points and these are now discarded by the execution of a cut (or other pruning of the search tree such as if-then-else).
exception(Exception)
Goal raised the given Exception.
external_exception(Exception)
Goal succeeded with choice-points and these are now discarded due to an exception. For example:
?- setup_call_catcher_cleanup(true, (X=1;X=2),
                              Catcher, writeln(Catcher)),
   throw(ball).
external_exception(ball)
ERROR: Unhandled exception: Unknown message: ball

call_cleanup(:Goal, :Cleanup)
Same as setup_call_cleanup(true, Goal, Cleanup). This is provided for compatibility with a number of other Prolog implementations only. Do not use call_cleanup/2 if you perform side-effects prior to calling that will be undone by Cleanup. Instead, use setup_call_cleanup/3 with an appropriate first argument to perform those side-effects.
call_cleanup(:Goal, +Catcher, :Cleanup)
Same as setup_call_catcher_cleanup(true, Goal, Catcher, Cleanup). The same warning as for call_cleanup/2 applies.