Catch/throw/recover -- until its suceeds

I am reviewing again the catch / throw / recover predicates.

I need to write a predicate that may need recovery actions repeatedly to occur.

So, a first call, could throw an exception, which is then handled by a recover action, which eventually would call the predicate again, and this second call (attempt) which then again could throw an exception, requiring a recover, and so on, until the call succeeds without exception.

Can this be achieved with one catch/throw/recover or do i need to somehow recursively recall the predicate with separate catch calls.

thanks,

Dan

The following code demonstrates such functionality
also including a count which might be useful
during recover when deciding to continue or give up .

~~~~~~~~ %&ZHcx;. ~~~~~~~~

/*
?- demo .
question about recover(user:task,1,ball(oops))  .
continue [yn] ? y

question about recover(user:task,2,ball(oops))  .
continue [yn] ? |: y

question about recover(user:task,3,ball(oops))  .
continue [yn] ? |: n

gaveup(user:task,3,ball(oops))  .
true.
*/
:- meta_predicate try(:,:,:) .

/*
Try to perform the goal supplied by _try_ ;
if that _try_ goal does throw a ball ,
then call _recover_ with 1st argument the _try_ and 2nd argument the _count_ and 3rd argument the _ball_ .
If that _recover_ does evaluate ``true`` then call the _try_ goal again ;
otherwise if that _recover_ does evaluate false
then call _gaveup_ with 1st argument the _try_ and 2nd argument the _count_ and 3rd argument the _ball_ .
*/

try(_try_,_recover_,_gaveup_) :-
(
    try(_try_,_recover_,_gaveup_,0)
) .

try(_try_,_recover_,_gaveup_,_count_) :-
(
    _COUNT_ is _count_ + 1 ,
    catch(_try_,_ball_,try(_try_,_recover_,_gaveup_,_COUNT_,_ball_))
)  .

:- meta_predicate try(:,:,:,-) .

try(_try_,_recover_,_gaveup_,_count_,_ball_) :-
(
    call(_recover_,_try_,_count_,_ball_) ->
    try(_try_,_recover_,_gaveup_,_count_) ;
    call(_gaveup_,_try_,_count_,_ball_)
)  .
demo :-
(
    try(task,recover,gaveup)
)  .

task :-
(
    throw(ball(oops))
)  .

:- meta_predicate recover(:,-,-) .

recover(_try_,_ball_,_count_) :- repeat ,
(
    format('~Nquestion about ~q  .',[recover(_try_,_ball_,_count_)]) ,
    format('~Ncontinue [yn] ? ',[]) ,
    flush_output ,
    get(_code_) ,
    (
        (_code_ = 0'y ; _code_ = 0'Y) -> ! , true ;
        (_code_ = 0'n ; _code_ = 0'N) -> ! , false ;
        false
    )
)  .

:- meta_predicate gaveup(:,-,-) .

gaveup(_try_,_ball_,_count_) :-
(
    format('~N~q  .',[gaveup(_try_,_ball_,_count_)])
)  .

Hi zhCX

Here is a proposal for you that I have used myself, I made it from my code quickly, so it might have problems. You will have to edit it for your needs and test it.

Call tst/0 and after 5 exceptions it throws a ‘really bad situation’

The exception catcher receives also the code that caused an exception and that code is re-used to call again. Prolog has the ability to pass code around.

This example worked on my Windows 10 and SWI-Prolog version (threaded, 64 bits, version 8.1.0).

exc( 'guru meditation',(_=5, _Code)):-
    !,
    throw('really bad situation').

exc( 'guru meditation',(_=Number,Code)):-

   %recovering code needs to be but here

    succ(Number,SNumber),
    W2= (_=SNumber,Code),

    catch( W2,Exception,exc(Exception,W2)).


tst:-
    W=(throw('guru meditation'),writeln(hello)), %code with an exception
    catch( W,Exception,exc(Exception,( _Number=0,W))).