TRUE
on success. PL_unify()
does not evaluate attributed variables (see section
8.1), it merely schedules the goals associated with the attributes
to be executed after the foreign predicate succeeds.221Goal
associated with attributes may be non-deterministic, which we cannot
handle from a callback. A callback could also result in deeply nested
mutual recursion between C and Prolog and eventually trigger a C stack
overflow.
Care is needed if PL_unify()
returns FALSE
and the foreign function does not immediately
return to Prolog with
FALSE
. Unification may perform multiple changes to either
t1 or t2. A failing unification may have created
bindings before failure is detected. Already created bindings are
not undone. For example, calling PL_unify()
on a(X, a)
and
a(c,b)
binds X to c
and fails when
trying to unify
a
to b
. If control remains in C or if we want
to return success to Prolog, we must undo such bindings. In
addition, PL_unify()
may have failed on an exception, typically a resource (stack)
overflow. This can be tested using PL_exception(),
passing 0 (zero) for the query-id argument. Foreign functions that
encounter an exception must return FALSE
to Prolog as soon
as possible or call PL_clear_exception()
if they wish to ignore the exception. Note that there can only be an
exception if PL_unify()
returned FALSE
.
In some scenarios we need to undo partial unifications.
Suppose we have a database that contains Prolog terms and we run a query
over this database. We must succeed on the first successful unification.
If a unification is not successful, we must stop if there is an
exception or undo the partial unification and try again. Suppose our
database contains f(a,1)
and f(b,2)
and our
query is f(A,2)
. This should succeed with A =
b
, but the first unification binds A to a
before failing to unify 1 with 2.
static foreign_t find_in_db(term_t target) { fid_t fid = PL_open_foreign_frame(); term_t candidate = PL_new_term_ref(); while(get_from_my_database(candidate)) { if ( PL_unify(candidate, target) ) /* found */ { PL_close_foreign_frame(fid); return TRUE; } else if ( PL_exception(0) ) /* error */ { PL_close_foreign_frame(fid); return FALSE; } PL_rewind_foreign_frame(fid); /* try next */ } PL_close_foreign_frame(fid); /* not found */ return FALSE; }
This code is only needed if the foreign predicate does not return
immediately to Prolog when PL_unify()
fails - there is an implicit frame around the entire predicate, and
returning FALSE
undoes all bindings when that frame is
closed.