exception handling
Fergus Henderson
fjh at cs.mu.oz.au
Fri Feb 28 02:20:21 AEDT 1997
Hi,
Obviously I have too much time on my hands, so (following my discussion
with Bart on this topic last week) I implemented exception handling.
I'm still not sure whether we want exception handling from
a language design perspective. I think it would be a good idea to
implement destructive update properly before we make any definite
decisions on exception handling, because these two features interact
in nasty ways.
Because of the above, I don't plan to commit this change in the
near future, at least not as part of the baseline.
(Perhaps we should have a special directory in the CVS repository
for things like this?)
Still, an implementation of exception handling is a nice thing
to have lying around for a time when we might need it.
Comments/feedback welcome.
The change is very self-contained: I didn't modify any of the
existing files, I just added a new file `exception.m' which
implements the library predicates for throwing and catching exceptions.
If/when this change hits the big-time, it might also be a good idea to
change error/1 in library/require.m to throw an exception rather than
aborting.
library/exception.m:
New file: adds support for exception handling.
tests/hard_coded/test_exceptions.m:
New file: some tests of exception handling.
%---------------------------------------------------------------------------%
% Copyright (C) 1997 The University of Melbourne.
% This file may only be copied under the terms of the GNU Library General
% Public License - see the file COPYING.LIB in the Mercury distribution.
%---------------------------------------------------------------------------%
% File: exception.m.
% Main author: fjh.
% Status: not supported!
% This file contains experimental code for exception handling.
% Do not use it.
% XXX this won't work in non-gc grades, because it doesn't have code to
% deep_copy() the thrown exception to the solutions_heap and back;
% instead it just calls fatal_error().
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- module exception.
:- interface.
:- import_module std_util, io.
%
% throw(Exception):
% Throw the specified exception.
%
:- pred throw(T).
:- mode throw(in) is erroneous.
%
% try(Goal, Result):
% Call Goal(R).
% If Goal(R) fails, fail.
% If Goal(R) succeeds, succeed with Result = succeeded(R).
% If Goal(R) throws an exception E, succeed with Result = exception(E).
%
:- type exception_result(T)
---> succeeded(T)
; exception(univ).
/***************
Sorry, the generic try/2 is not yet implemented.
Currently you must use `try_det', `try_semidet', etc.
:- pred try(pred(T), exception_result(T)).
:- mode try(pred(out) is det, out) is det.
:- mode try(pred(out) is semidet, out) is semidet.
:- mode try(pred(out) is cc_multi, out) is cc_multi.
:- mode try(pred(out) is cc_nondet, out) is cc_nondet.
:- mode try(pred(out) is multi, out) is multi.
:- mode try(pred(out) is nondet, out) is nondet.
***************/
:- pred try_det(pred(T), exception_result(T)).
:- mode try_det(pred(out) is det, out) is det.
:- pred try_semidet(pred(T), exception_result(T)).
:- mode try_semidet(pred(out) is semidet, out) is semidet.
:- pred try_cc_multi(pred(T), exception_result(T)).
:- mode try_cc_multi(pred(out) is cc_multi, out) is cc_multi.
:- pred try_cc_nondet(pred(T), exception_result(T)).
:- mode try_cc_nondet(pred(out) is cc_nondet, out) is cc_nondet.
:- pred try_multi(pred(T), exception_result(T)).
:- mode try_multi(pred(out) is multi, out) is multi.
:- pred try_nondet(pred(T), exception_result(T)).
:- mode try_nondet(pred(out) is nondet, out) is nondet.
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- implementation.
throw(T) :-
type_to_univ(T, Univ),
builtin_throw(Univ).
/*********************
% This doesn't work, due to
% bash$ mc exception.m
% Software error: sorry, not implemented: taking address of pred
% `wrap_success/2' with multiple modes.
try(Goal, Result) :-
builtin_catch(wrap_success(Goal), wrap_exception, Result).
:- pred wrap_success(pred(T), exception_result(T)) is det.
:- mode wrap_success(pred(out) is det, out) is det.
:- mode wrap_success(pred(out) is semidet, out) is semidet.
:- mode wrap_success(pred(out) is cc_multi, out) is cc_multi.
:- mode wrap_success(pred(out) is cc_nondet, out) is cc_nondet.
:- mode wrap_success(pred(out) is multi, out) is multi.
:- mode wrap_success(pred(out) is nondet, out) is nondet.
wrap_success(Goal, succeeded(Result)) :- Goal(Result).
*********************/
try_det(Goal, Result) :-
builtin_catch(wrap_success_det(Goal), wrap_exception, Result).
try_semidet(Goal, Result) :-
builtin_catch(wrap_success_semidet(Goal), wrap_exception, Result).
try_cc_multi(Goal, Result) :-
builtin_catch(wrap_success_cc_multi(Goal), wrap_exception, Result).
try_cc_nondet(Goal, Result) :-
builtin_catch(wrap_success_cc_nondet(Goal), wrap_exception, Result).
try_multi(Goal, Result) :-
builtin_catch(wrap_success_multi(Goal), wrap_exception, Result).
try_nondet(Goal, Result) :-
builtin_catch(wrap_success_nondet(Goal), wrap_exception, Result).
:- pred wrap_success_det(pred(T), exception_result(T)) is det.
:- mode wrap_success_det(pred(out) is det, out) is det.
wrap_success_det(Goal, succeeded(Result)) :- Goal(Result).
:- pred wrap_success_semidet(pred(T), exception_result(T)) is det.
:- mode wrap_success_semidet(pred(out) is semidet, out) is semidet.
wrap_success_semidet(Goal, succeeded(Result)) :- Goal(Result).
:- pred wrap_success_cc_multi(pred(T), exception_result(T)) is det.
:- mode wrap_success_cc_multi(pred(out) is cc_multi, out) is cc_multi.
wrap_success_cc_multi(Goal, succeeded(Result)) :- Goal(Result).
:- pred wrap_success_cc_nondet(pred(T), exception_result(T)) is det.
:- mode wrap_success_cc_nondet(pred(out) is cc_nondet, out) is cc_nondet.
wrap_success_cc_nondet(Goal, succeeded(Result)) :- Goal(Result).
:- pred wrap_success_multi(pred(T), exception_result(T)) is det.
:- mode wrap_success_multi(pred(out) is multi, out) is multi.
wrap_success_multi(Goal, succeeded(Result)) :- Goal(Result).
:- pred wrap_success_nondet(pred(T), exception_result(T)) is det.
:- mode wrap_success_nondet(pred(out) is nondet, out) is nondet.
wrap_success_nondet(Goal, succeeded(Result)) :- Goal(Result).
:- pred wrap_exception(univ::in, exception_result(T)::out) is det.
wrap_exception(Exception, exception(Exception)).
%-----------------------------------------------------------------------------%
:- pred builtin_throw(univ).
:- mode builtin_throw(in) is erroneous.
:- type handler(T) == pred(univ, T).
:- inst handler == (pred(in, out) is det).
:- pred builtin_catch(pred(T), handler(T), T).
:- mode builtin_catch(pred(out) is det, in(handler), out) is det.
:- mode builtin_catch(pred(out) is semidet, in(handler), out) is semidet.
:- mode builtin_catch(pred(out) is cc_multi, in(handler), out) is cc_multi.
:- mode builtin_catch(pred(out) is cc_nondet, in(handler), out) is cc_nondet.
:- mode builtin_catch(pred(out) is multi, in(handler), out) is multi.
:- mode builtin_catch(pred(out) is nondet, in(handler), out) is nondet.
% builtin_throw and builtin_catch are implemented below using
% hand-coded low-level C code.
:- external(builtin_throw/1).
:- external(builtin_catch/3).
%-----------------------------------------------------------------------------%
:- pragma c_code("
enum CodeModel { MODEL_DET, MODEL_SEMI, MODEL_NON };
Define_extern_entry(mercury__exception__builtin_catch_3_0); /* det */
Define_extern_entry(mercury__exception__builtin_catch_3_1); /* semidet */
Define_extern_entry(mercury__exception__builtin_catch_3_2); /* cc_multi */
Define_extern_entry(mercury__exception__builtin_catch_3_3); /* cc_nondet */
Define_extern_entry(mercury__exception__builtin_catch_3_4); /* multi */
Define_extern_entry(mercury__exception__builtin_catch_3_6); /* nondet */
Define_extern_entry(mercury__exception__builtin_throw_1_0);
Define_extern_entry(exception_handler_do_fail);
/* the following are defined in runtime/call.mod */
Declare_entry(do_call_det_closure);
Declare_entry(do_call_semidet_closure);
Declare_entry(do_call_nondet_closure);
Declare_label(mercury__exception__builtin_catch_3_2_i1);
Declare_label(mercury__exception__builtin_catch_3_2_i2);
Declare_label(mercury__exception__builtin_catch_3_3_i1);
Declare_label(mercury__exception__builtin_catch_3_3_i2);
Declare_label(mercury__exception__builtin_catch_3_5_i1);
Declare_label(mercury__exception__builtin_catch_3_5_i2);
#ifndef COMPACT_ARGS
Declare_label(mercury__exception__builtin_throw_1_0_i1);
Declare_label(mercury__exception__builtin_throw_1_0_i2);
Declare_label(mercury__exception__builtin_throw_1_0_i3);
#endif
BEGIN_MODULE(exceptions_module)
init_entry(mercury__exception__builtin_catch_3_0);
init_entry(mercury__exception__builtin_catch_3_1);
init_entry(mercury__exception__builtin_catch_3_2);
init_entry(mercury__exception__builtin_catch_3_3);
init_entry(mercury__exception__builtin_catch_3_4);
init_entry(mercury__exception__builtin_catch_3_5);
init_label(mercury__exception__builtin_catch_3_2_i1);
init_label(mercury__exception__builtin_catch_3_2_i2);
init_label(mercury__exception__builtin_catch_3_3_i1);
init_label(mercury__exception__builtin_catch_3_3_i2);
init_label(mercury__exception__builtin_catch_3_5_i1);
init_label(mercury__exception__builtin_catch_3_5_i2);
init_label(mercury__exception__builtin_throw_1_0);
#ifndef COMPACT_ARGS
init_label(mercury__exception__builtin_throw_1_0_i1);
init_label(mercury__exception__builtin_throw_1_0_i2);
init_label(mercury__exception__builtin_throw_1_0_i3);
#endif
init_entry(exception_handler_do_fail);
BEGIN_CODE
/*
** builtin_catch(Goal, Handler, Result)
** call Goal(R).
** if succeeds, set Result = R.
** if throws an exception, call Handler(Result).
**
** This is the model_det version.
** On entry, we have a type_info (which we don't use) in r1,
** the Goal to execute in r2 and the Handler in r3.
** On exit, we should put Result in r1 (with COMPACT_ARGS) or r4.
*/
Define_entry(mercury__exception__builtin_catch_3_0); /* det */
Define_entry(mercury__exception__builtin_catch_3_2); /* cc_multi */
/*
** Create a handler on the stack with the special redoip
** of `exception_handler_do_fail' (we'll look for this redoip
** when unwinding the nondet stack in builtin_throw/1),
** and save the stuff we will need if an exception is thrown.
*/
mkframe(""builtin_catch/3 [model_det]"", 4,
ENTRY(exception_handler_do_fail));
framevar(0) = MODEL_DET;
framevar(1) = r3; /* save the Handler closure */
framevar(2) = (Word) sp; /* save the det stack pointer */
mark_hp(framevar(3)); /* save the heap pointer (if any) */
/*
** Now we need to create another frame.
** This is so that we can be sure that no-one will hijack
** the redoip of the special frame we created above.
** (The compiler sometimes generates ``hijacking'' code that saves
** the topmost redoip on the stack, and temporarily replaces it
** with a new redoip that will do some processing on failure
** before restoring the original redoip. This would cause
** problems when doing stack unwinding in builtin_throw/1,
** because we wouldn't be able to find the special redoip.
** But code will only ever hijack the topmost frame, so we
** can avoid this by creating a second frame above the special
** frame.)
*/
succip = LABEL(mercury__exception__builtin_catch_3_2_i1);
mkframe(""builtin_catch_2/1 [model_det]"", 0, ENTRY(do_fail));
/*
** Now call `Goal(Result)'.
*/
r1 = r2; /* The Goal to call */
r2 = 0; /* Zero additional input arguments */
r3 = 1; /* One output argument */
call(ENTRY(do_call_det_closure),
LABEL(mercury__exception__builtin_catch_3_2_i2),
LABEL(mercury__exception__builtin_catch_3_2));
Define_label(mercury__exception__builtin_catch_3_2_i2);
update_prof_current_proc(LABEL(mercury__exception__catch_3_2));
/*
** On exit from do_call_det_closure, Result is in r1
*/
#ifndef COMPACT_ARGS
r4 = r1;
#endif
succeed_discard();
Define_label(mercury__exception__builtin_catch_3_2_i1);
succeed_discard();
/*
** builtin_catch(Goal, Handler, Result)
** call Goal(R).
** if succeeds, set Result = R.
** if fails, fail.
** if throws an exception, call Handler(Result).
**
** This is the model_semi version.
** With COMPACT_ARGS,
** on entry, we have a type_info (which we don't use) in r1,
** the Goal to execute in r2 and the Handler in r3,
** and on exit, we should put Result in r2.
** Without COMPACT_ARGS,
** on entry, we have a type_info (which we don't use) in r2,
** the Goal to execute in r3 and the Handler in r4,
** and on exit, we should put Result in r5.
*/
Define_entry(mercury__exception__builtin_catch_3_1); /* semidet */
Define_entry(mercury__exception__builtin_catch_3_3); /* cc_nondet */
/*
** Create a handler on the stack with the special redoip
** of `exception_handler_do_fail' (we'll look for this redoip
** when unwinding the nondet stack in builtin_throw/1),
** and save the stuff we will need if an exception is thrown.
*/
mkframe(""builtin_catch/3 [model_semi]"", 4,
ENTRY(exception_handler_do_fail));
framevar(0) = MODEL_SEMI;
#ifdef COMPACT_ARGS
framevar(1) = r3; /* save the Handler closure */
#else
framevar(1) = r4; /* save the Handler closure */
#endif
framevar(2) = (Word) sp; /* save the det stack pointer */
mark_hp(framevar(3)); /* save the heap pointer (if any) */
/*
** Now we need to create another frame.
** This is so that we can be sure that no-one will hijack
** the redoip of the special frame we created above.
** (The compiler sometimes generates ``hijacking'' code that saves
** the topmost redoip on the stack, and temporarily replaces it
** with a new redoip that will do some processing on failure
** before restoring the original redoip. This would cause
** problems when doing stack unwinding in builtin_throw/1,
** because we wouldn't be able to find the special redoip.
** But code will only ever hijacks the topmost frame, so we
** can avoid this by creating a second frame above the special
** frame.)
*/
succip = LABEL(mercury__exception__builtin_catch_3_3_i1);
mkframe(""builtin_catch_2/1 [model_semi]"", 0, ENTRY(do_fail));
/*
** Now call `Goal(Result)'.
*/
#ifdef COMPACT_ARGS
r1 = r2; /* The Goal to call */
#else
r1 = r3; /* The Goal to call */
#endif
r2 = 0; /* Zero additional input arguments */
r3 = 1; /* One output argument */
call(ENTRY(do_call_semidet_closure),
LABEL(mercury__exception__builtin_catch_3_3_i2),
LABEL(mercury__exception__builtin_catch_3_3));
Define_label(mercury__exception__builtin_catch_3_3_i2);
update_prof_current_proc(LABEL(mercury__exception__catch_3_3));
/*
** On exit from do_call_semidet_closure, the success/failure
** indicator is in r1, and Result is in r2.
** Note that we call succeed_discard() to exit regardless
** of whether r1 is true or false. We just return the r1 value
** back to our caller.
*/
#ifndef COMPACT_ARGS
r5 = r2;
#endif
succeed_discard();
Define_label(mercury__exception__builtin_catch_3_3_i1);
succeed_discard();
/*
** builtin_catch(Goal, Handler, Result)
** call Goal(R).
** if succeeds, set Result = R.
** if fails, fail.
** if throws an exception, call Handler(Result).
**
** This is the model_non version.
** On entry, we have a type_info (which we don't use) in r1,
** the Goal to execute in r2 and the Handler in r3.
** On exit, we should put Result in r1 (with COMPACT_ARGS) or r3.
*/
Define_entry(mercury__exception__builtin_catch_3_4); /* multi */
Define_entry(mercury__exception__builtin_catch_3_5); /* nondet */
/*
** Create a handler on the stack with the special redoip
** of `exception_handler_do_fail' (we'll look for this redoip
** when unwinding the nondet stack in builtin_throw/1),
** and save the stuff we will need if an exception is thrown.
*/
mkframe(""builtin_catch/3 [nondet]"", 4,
ENTRY(exception_handler_do_fail));
framevar(0) = MODEL_NON;
framevar(1) = r3; /* save the Handler closure */
framevar(2) = (Word) sp; /* save the det stack pointer */
mark_hp(framevar(3)); /* save the heap pointer (if any) */
/*
** Now we need to create another frame.
** This is so that we can be sure that no-one will hijack
** the redoip of the special frame we created above.
** (The compiler sometimes generates ``hijacking'' code that saves
** the topmost redoip on the stack, and temporarily replaces it
** with a new redoip that will do some processing on failure
** before restoring the original redoip. This would cause
** problems when doing stack unwinding in builtin_throw/1,
** because we wouldn't be able to find the special redoip.
** But code will only ever hijacks the topmost frame, so we
** can avoid this by creating a second frame above the special
** frame.)
*/
succip = LABEL(mercury__exception__builtin_catch_3_5_i1);
mkframe(""builtin_catch_2/1 [nondet]"", 0, ENTRY(do_fail));
/*
** Now call `Goal(Result)'.
*/
r1 = r2; /* the Goal to call */
r2 = 0; /* Zero additional input arguments */
r3 = 1; /* One output argument */
call(ENTRY(do_call_nondet_closure),
LABEL(mercury__exception__builtin_catch_3_5_i2),
LABEL(mercury__exception__builtin_catch_3_5));
Define_label(mercury__exception__builtin_catch_3_5_i2);
update_prof_current_proc(LABEL(mercury__exception__catch_3_5));
/*
** On exit from do_call_nondet_closure, Result is in r1
*/
#ifndef COMPACT_ARGS
r3 = r1;
#endif
succeed();
Define_label(mercury__exception__builtin_catch_3_5_i1);
succeed();
/*
** builtin_throw(Exception):
** Throw the specified exception.
** That means unwinding the nondet stack until we find a handler, and then
** calling Handler(Result).
**
** On entry, we have Exception in r1.
*/
Define_entry(mercury__exception__builtin_throw_1_0);
{
Word exception = r1;
Word handler;
enum CodeModel catch_code_model;
/*
** Search the nondet stack for an exception handler,
** i.e. a frame whose redoip is `exception_handler_do_fail'
** (one created by `builtin_catch').
** N.B. We search down the `succfr' chain, not the `prevfr' chain;
** this ensures that we only find handlers installed by our callers,
** not handlers installed by procedures that we called but which
** are still on the nondet stack because they choice points behind.
*/
do {
curfr = cursuccfr;
if (curfr < nondetstack_zone->min) {
fatal_error(""builtin_throw/1: uncaught exception"");
}
} while (curredoip != ENTRY(exception_handler_do_fail));
/*
** Save the handler we found, reset the det stack to and heap,
** pop the final exception handler frame off the nondet stack,
** and reset the nondet stack top.
*/
succip = cursuccip;
catch_code_model = framevar(0);
handler = framevar(1);
sp = (Word) framevar(2); /* reset the det stack pointer */
restore_hp(framevar(3)); /* reset the heap pointer (if any) */
maxfr = curprevfr;
curfr = maxfr;
#ifndef CONSERVATIVE_GC
/* XXX we need to do deep_copy() on the exception object */
fatal_error(""throw/1 not implemented for non-gc grades"");
#endif
/*
** Now invoke the handler that we found, as `Handler(Result)',
*/
r1 = handler; /* get the Handler closure */
r2 = 1; /* One additional input argument */
r3 = 1; /* One output argument */
r4 = exception; /* This is our one input argument */
#ifdef COMPACT_ARGS
/*
** If the catch was semidet, we need to set the success indicator
** r1 to TRUE and return the result in r2; otherwise, we return
** the result in r1, which is where do_call_det_closure puts it,
** so we can to a tailcall.
*/
if (catch_code_model != MODEL_SEMI) {
tailcall(ENTRY(do_call_det_closure),
LABEL(mercury__exception__builtin_throw_1_0));
}
push(succip);
call(ENTRY(do_call_det_closure),
LABEL(mercury__exception__builtin_throw_1_0_i1),
LABEL(mercury__exception__builtin_throw_1_0));
}
Define_label(mercury__exception__builtin_throw_1_0_i1);
r2 = r1;
r1 = TRUE;
succip = (Code *) pop();
proceed();
#else /* not COMPACT_ARGS */
push(succip);
push(catch_code_model);
call(ENTRY(do_call_det_closure),
LABEL(mercury__exception__builtin_throw_1_0_i1)),
LABEL(mercury__exception__builtin_throw_1_0));
}
Define_label(mercury__exception__builtin_throw_1_0_i1);
/* we've just returned from do_call_det_closure */
catch_code_model = pop();
if (catch_code_model == MODEL_SEMI) {
r5 = r1;
else {
r4 = r1;
}
succip = (Code *) pop();
proceed();
#endif /* not COMPACT_ARGS */
Define_entry(exception_handler_do_fail);
/*
** `exception_handler_do_fail' is the same as `do_fail':
** it just invokes fail(). The reason we don't just use
** `do_fail' for this is that when unwinding the stack we
** check for a redoip of `exception_handler_do_fail' and
** handle it specially.
*/
fail();
END_MODULE
/* suppress gcc -Wmissing-decls warning */
void mercury_sys_init_exceptions(void);
void mercury_sys_init_exceptions(void) {
exceptions_module();
}
").
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
% Copyright (C) 1997 The University of Melbourne.
% This file may only be copied under the terms of the GNU Library General
% Public License - see the file COPYING.LIB in the Mercury distribution.
%-----------------------------------------------------------------------------%
% File: test_exceptions.m.
% Main author: fjh.
% Test cases for exception handling.
% XXX we should test nested exception handlers.
%-----------------------------------------------------------------------------%
:- module test_exceptions.
:- interface.
:- import_module io.
:- pred main(io__state::di, io__state::uo) is cc_multi.
:- implementation.
:- import_module std_util.
:- import_module exception.
main -->
{ try_det(det_throw, DetThrowResult) },
write("det_throw: "), write(DetThrowResult), nl,
{ try_det(det_succeed, DetSucceedResult) },
write("det_succeed: "), write(DetSucceedResult), nl,
(if { try_semidet(semidet_throw, SemidetThrowResult) } then
write("semidet_throw: "), write(SemidetThrowResult), nl
else
write("semidet_throw failed"), nl
),
(if { try_semidet(semidet_succeed, SemidetSucceedResult) } then
write("semidet_succeed: "), write(SemidetSucceedResult), nl
else
write("semidet_succeed failed"), nl
),
(if { try_semidet(semidet_fail, SemidetFailResult) } then
write("semidet_fail: "), write(SemidetFailResult), nl
else
write("semidet_fail failed"), nl
),
{ try_cc_multi(cc_multi_throw, CCMultiThrowResult) },
write("cc_multi_throw: "), write(CCMultiThrowResult), nl,
{ try_cc_multi(cc_multi_succeed, CCMultiSucceedResult) },
write("cc_multi_succeed: "), write(CCMultiSucceedResult), nl,
(if { try_cc_nondet(cc_nondet_throw, CCNondetThrowResult) } then
write("cc_nondet_throw: "), write(CCNondetThrowResult), nl
else
write("cc_nondet_throw failed"), nl
),
(if { try_cc_nondet(cc_nondet_succeed, CCNondetSucceedResult) } then
write("cc_nondet_succeed: "), write(CCNondetSucceedResult), nl
else
write("cc_nondet_succeed failed"), nl
),
(if { try_cc_nondet(cc_nondet_fail, CCNondetFailResult) } then
write("cc_nondet_fail: "), write(CCNondetFailResult), nl
else
write("cc_nondet_fail failed"), nl
),
{ solutions(try_multi(multi_throw), MultiThrowResult) },
write("multi_throw: "), write(MultiThrowResult), nl,
{ solutions(try_multi(multi_succeed), MultiSucceedResult) },
write("multi_succeed: "), write(MultiSucceedResult), nl,
{ solutions(try_multi(multi_succeed_then_throw),
MultiSucceedThenThrowResult) },
write("multi_succeed_then_throw: "),
write(MultiSucceedThenThrowResult), nl,
{ solutions(try_nondet(nondet_throw), NondetThrowResult) },
write("nondet_throw: "), write(NondetThrowResult), nl,
{ solutions(try_nondet(nondet_succeed), NondetSucceedResult) },
write("nondet_succeed: "), write(NondetSucceedResult), nl,
{ solutions(try_nondet(nondet_fail), NondetFailResult) },
write("nondet_fail: "), write(NondetFailResult), nl,
{ solutions(try_nondet(nondet_succeed_then_throw),
NondetSucceedThenThrowResult) },
write("nondet_succeed_then_throw: "),
write(NondetSucceedThenThrowResult), nl.
:- pred det_throw(string::out) is det.
det_throw(_) :- throw("det_throw").
:- pred semidet_throw(string::out) is semidet.
semidet_throw(_) :- throw("semidet_throw").
:- pred nondet_throw(string::out) is nondet.
nondet_throw(_) :- throw("nondet_throw").
:- pred multi_throw(string::out) is multi.
multi_throw(_) :- throw("multi_throw").
:- pred cc_nondet_throw(string::out) is cc_nondet.
cc_nondet_throw(_) :- throw("cc_nondet_throw").
:- pred cc_multi_throw(string::out) is cc_multi.
cc_multi_throw(_) :- throw("cc_multi_throw").
:- pred det_succeed(string::out) is det.
det_succeed("det_succeed").
:- pred semidet_succeed(string::out) is semidet.
semidet_succeed("semidet_succeed").
:- pred nondet_succeed(string::out) is nondet.
nondet_succeed("nondet_succeed 1").
nondet_succeed("nondet_succeed 2").
:- pred multi_succeed(string::out) is multi.
multi_succeed("multi_succeed 1").
multi_succeed("multi_succeed 2").
:- pred cc_nondet_succeed(string::out) is cc_nondet.
cc_nondet_succeed("cc_nondet_succeed").
cc_nondet_succeed("cc_nondet_succeed 2").
:- pred cc_multi_succeed(string::out) is cc_multi.
cc_multi_succeed("cc_multi_succeed").
cc_multi_succeed("cc_multi_succeed 2").
:- pred semidet_fail(string::out) is semidet.
semidet_fail("semidet_fail") :- fail.
:- pred nondet_fail(string::out) is nondet.
nondet_fail("nondet_fail 1") :- fail.
nondet_fail("nondet_fail 2") :- fail.
:- pred cc_nondet_fail(string::out) is cc_nondet.
cc_nondet_fail("cc_nondet_fail") :- fail.
cc_nondet_fail("cc_nondet_succeed 2") :- fail.
:- pred nondet_succeed_then_throw(string::out) is nondet.
nondet_succeed_then_throw("nondet_succeed_then_throw 1").
nondet_succeed_then_throw("nondet_succeed_then_throw 2").
nondet_succeed_then_throw(_) :- throw("nondet_succeed_then_throw 3").
nondet_succeed_then_throw("nondet_succeed_then_throw 4").
:- pred multi_succeed_then_throw(string::out) is multi.
multi_succeed_then_throw("multi_succeed_then_throw 1").
multi_succeed_then_throw("multi_succeed_then_throw 2").
multi_succeed_then_throw(_) :- throw("multi_succeed_then_throw 3").
multi_succeed_then_throw("multi_succeed_then_throw 4").
--
Fergus Henderson <fjh at cs.mu.oz.au> | "I have always known that the pursuit
WWW: <http://www.cs.mu.oz.au/~fjh> | of excellence is a lethal habit"
PGP: finger fjh at 128.250.37.3 | -- the last words of T. S. Garp.
More information about the developers
mailing list