I am trying to define a relation callto_status(Goal, Status)
that always succeeds and unifies Status according to the result of calling Goal
(in other words, I would like to implement a reified version of call_with_inference_limit/3
). My implementation uses SWI's call_with_inference_limit/3
which has the same interface as call_with_time_limit/3
(which should make it work in that case as well). The implementation of call_with_..._limit
does not backtrack, so I thought it best not to give the impression of reporting an answer substiution for the goal.
I introduced the helper-predicate derivable_st
for readability. It handles the success and timeout case but fails otherwise.
% if Goal succeeds, succeed with Status = true,
% if Goal times out, succeed with Status = timeout
% if Goal fails, fail
derivable_st(Goal, Status) :-
T = 10000, % set inference limit
% copy_term(Goal, G), % work on a copy of Goal, we don't want to report an answer substitution
call_with_inference_limit(G, T, R), % actual call to set inference limit
( R == !
-> Status = true % succeed deterministically, status = true
; R == true
-> Status = true % succeed non-deterministically, status = true
; ( R == inference_limit_exceeded % timeout
-> (
!, % make sure we do not backtrack after timeout
Status = timeout % status = timeout
)
; throw(unhandled_case) % this should never happen
)
).
The main predicate wraps around derivable_st
and handles the failure case and possibly thrown exceptions (if any). We might want to single out stack overflows (that happen in case of too high inference limits) but for now we just report any exception.
% if Goal succeeds, succeed with Status = true,
% if Goal times out, succeed with Status = timeout
% if Goal fails, succeed with Status = false
% if Goal throws an error, succeed with Status = exception(The_Exception)
% Goal must be sufficiently instantiated for call(Goal) but will stay unchanged
callto_status(Goal, Status) :-
catch(( derivable_st(Goal, S) % try to derive Goal
-> Status = S % in case of success / timeout, pass status on
; Status = false % in case of failure, pass failure status on, but succeed
),
Exception,
Status = exception(Exception) % wrap the exception into a status term
).
The predicate works for some simple test cases:
?- callto_reif( length(N,X), Status).
Status = true.
?- callto_reif( false, Status).
Status = false.
?- callto_reif( (length(N,X), false), Status).
Status = timeout.
My question now is a bit vague: does this predicate do what I claim it does? Do you see any mistakes / points of improvement? I am grateful for any input!
Edit: as suggested by @false, commented out copy_term/2
L=[], N=0, Status=true
as the only solution, which is misleading. – Tal