pack_errors.pl -- Contextual error handling for packs

This is a stoics.infrastructure pack for

  1. mid-level management handling of pack errors,
  2. provide a simple, uniform way for informing users where the errors come from, and
  3. provide useful pre-canned errors.

As of 0.3 the library also provides type errors on top of must_be/2

The main aim is to create contextual version of messages that can be used from different packs. In addition the library has evolved to provide some error related predicates.

Wrapper errors

Providing the context:

pack_error(Pack, Pred, Vrb, Message)
spew Message in the context of Pack and Predicate. Vrb controls printing of context info. when Vrb is false there is no message about the pack the error originated from
pack_error(Pack, Pred, Message)
as above with Vrb = true
pack_error(Pack, Message)
as above without Predicate context

Prepacked errors

Argument errors (printing of Arg itself can be surpressed with prolog_flag(pack_errors_arg,false)- useful for long data). Poss is a list of positions and Args a list of arguments.

Other errors

Examples

?- throw( pack_error(os,arg_ground(3,name(_))) ).
ERROR: pack(os): Ground argument expected at position: 3 (found: name(_2064))
?- set_prolog_flag(pack_errors_arg,false).
?- throw( pack_error(os,arg_ground(3,name(_))) ).
ERROR: pack(os): Ground argument expected at position: 3
?- set_prolog_flag(pack_errors_arg,true).
?- throw( pack_error(os,os_pred/3,arg_ground(3,name(_))) ).
ERROR: os:os_pred/3: Ground argument expected at position: 3 (found: name(_2088))
?- throw( pack_error(os,os_pred/3,arg_enumerate(3,[a,b,c],d)) ).
ERROR: os:os_pred/3: Term at position: 3, is not one of: [a,b,c], (found: d)

?- throw( pack_error(mlu,lengths_mismatch(learners,predictions,3,4)) ).
ERROR: pack(mlu): Lists for learners and predictions have mismatching lengths: 3 and 4 respectively
?- throw( pack_error(mlu,k_fold_learn/3,lengths_mismatch(learners,predictions,3,4)) ).
ERROR: mlu:k_fold_learn/3: Lists for learners and predictions have mismatching lengths: 3 and 4 respectively
?- throw( pack_error(os,os_term/2,cast(abc('file.csv'),atom)) ).
ERROR: os:os_term/2: Cannot cast: abc(file.csv), to type: atom

Defining new pack errors

example file:

:- multifile( pack_errors:message/3 ).

pack_errors:message( fold_data_insufficient(Dlen,N) ) -->
    ['Insufficient length of data (~d) as ~d folds are required'-[Dlen,N]].
pack_errors:message( fold_data_residual(Dlen) ) -->
    ['Residual data of length: ~d while splitting folds'-[Dlen]].

Once the above has been loaded, try with

?- throw( fold_data_insufficient(10,20) ).
ERROR: Insufficient length of data (10) as 20 folds are required
?- throw( pack_error(mlu,fold_data_insufficient(10,20) ) ).
ERROR: pack(mlu): Insufficient length of data (10) as 20 folds are required
?- throw( pack_error(mlu,k_fold_learn/4,fold_data_insufficient(10,20) ) ).
ERROR: mlu:k_fold_learn/4: Insufficient length of data (10) as 20 folds are required

Pack info

The library listens to debug(pack_errors).

author
- nicos angelopoulos
version
- 0.1 2016/01/30
- 0.2 2016/02/24
- 0.3 2017/03/06
- 1.0 2018/03/18
See also
- http://stoics.org.uk/~nicos/sware/pack_errors
- lib predicates:
- caught/3 args, +Goal, +Call, +Opts
- ground/2, ground_binary/2 args: +Term, -Groundness
- throw/2 args: +Ball, +Opts
- type/2, type/3 args: +Type, +Term [, +Opts]
- pack_errors/0, pack_errors_version/2 args: +Version, +Date
- defined/3 args:
To be done
- equal length list checking
 caught(+Goal, +Error, +Opts)
Catches all errors and failure of Goal. The idea is that all non-successful executions are handled identical by the call. If Goal errors, the primary thrown ball is caught and discarded. If Goal errors or fails, behaviour depends on option value Report (see Opts below).

Opts

?- caught( fail, my_exception(on_data), true ).
ERROR: Unhandled exception: my_exception(on_data)

?- caught( fail, my_exception(on_data), [report(ignore)] ).
true

?- caught( fail, my_exception(on_data), [report(fail)] ).
false

?- caught( xyz, my_except(Ball), [ball(Ball)] ).
ERROR: Unhandled exception: my_except(error(existence_error(procedure,pack_errors:xyz/0),context(system:catch/3,_11198)))
See also
- throw/2
 ground(+Term, -Groundness)
 ground_binary(+Term, -Groundness)
Instantiates groundness of Term to Type. In ground_binary/2 Groundness partial and false are collapsed to false.

Groundness

true
Term is ground
false
Term is variable
partial
Term is partially instantiated

== ?- ground( abc, Abc ), ground( de(F), Def ), ground( GHI, Ghi ). Abc = true, Def = partial, Ghi = false.

?- ground_binary( abc, Abc ), ground_binary( de(F), Def ), ground_binary( GHI, Ghi ). Abc = true, Def = Ghi, Ghi = false.

 throw(+Error, +Opts)
An optionised version of throw/1. Error is not thrown if OnThrow==fail (see Opts below) and the call to throw/2 itself fails. When OnThrow==succeed Error is not thrown and the call itself succeeds. For all other values the default behaviour is that of OnThrow==error where is to thrown Error is assumed.

As of version 0.3 this should be the adviced entry point for throwing pack tracing balls.

Opts on_throw(OnThrow=error) one of [succeed,fail,error].
as_pack_err(Perr=true) true wraps Error, as a pack_error
pack(Pack=_) originator pack
pred(Pred=_) originator predicate
pack_format(Pfmt=short) output format for pack announcement
?- throw( my_error(x), true ).
ERROR: Unhandled exception: my_error(x)

?- throw( my_error(x), on_throw(succeed) ).
true.

?- throw( my_error(x), on_throw(fail) ).
false.

?- throw( my_error(x), on_throw(error) ).
ERROR: Unhandled exception: my_error(x)

?- throw( my_error(x), on_throw(whatelse) ).
ERROR: Unhandled exception: my_error(x)
author
- nicos angelopoulos
version
- 0.2 2017/3/6
- 0.3 2018/1/5 added tracer options: pack, pred & pack_format
 type(+Type, @Term)
 type(+Type, @Term, +Opts)
type/2 is a superset of must_be, in that it adds Type = @(Callable), (equiv: Type = call(Callable)), which will succeed iff call( Callable, Term ) succeeds. It also enhances must_be/2 by adding options . In the case of a call-wrapped type, the call to type/3 will succeed iff call(Callable,Term) succeeds.

Opts (unlisted is ok)

?- type( boolean, maybe ).
ERROR: Object of type: boolean, expected but found term: maybe
?- type( boolean, maybe, error(false) ).
false.
?- type( boolean, maybe, pack(sure) ).
ERROR: pack(sure): Object of type: boolean, expected but found term: maybe
?- type( boolean, maybe, [pack(sure),pred(lost/2)] ).
ERROR: sure:lost/2: Object of type: boolean, expected but found term: maybe
?- type( boolean, maybe, [pack(sure),pred(lost/2@3)] ).
ERROR: Syntax error: Operator expected
ERROR: type( boolean, maybe, [pack(sure),pred(lost/
ERROR: ** here **
ERROR: 2@3)] ) .
?- type( boolean, maybe, [pack(sure),pred(lost/2+3)] ).
ERROR: sure:lost/2+3: Object of type: boolean, expected but found term: maybe
?- type( boolean, maybe, [pack(sure),pred(1+lost/2)] ).
ERROR: sure:1+lost/2: Object of type: boolean, expected but found term: maybe
?- type( boolean, maybe, [pack(sure),pred(lost(arg1)/2)] ).
ERROR: sure:lost(arg1)/2: Object of type: boolean, expected but found term: maybe
 defined(+Pid, +From, +Opts)
Throws an error if Pid is not defined in current context.
From is the source from where Pid was supposed to be loaded.
This predicate can act independently (particularly with load(true))
or be combined with pack(lib)'s lib(suggests(Pack)) to, on-demand,
pinpoint to which library is missing and what
predicate within that pack is the deal breaker.

Note that pack(lib) also provides

lib(suggests(Pid,Load))

which is an alternative and more automatic way to achieve demand driven loading via hot-swapping.

:- lib(suggests(Pack))

silently fails if Pack is not present. This is intendent for dependendencies that do not impact major parts for the importing pack. Thus allow common use without grabbing all dependencies that may not be needed for a particular user.

Opts are passed to throw/2, except for: load(Load=false)
?- defined( abc/0, pack(b_real) ).
ERROR: Predicate: abc/0 is not defined (source apparently available at: pack(b_real); not asked to load)

?- defined( abc/0, false ).
ERROR: Predicate: abc/0 is not defined

?- defined( abc/0, false, pack(sourcey) ).
ERROR: sourcey:$unknown/0: Predicate: abc/0 is not defined

?- defined( abc/0, pack(b_real), [pack(sourcey),pred(foo/1;2)] ).
ERROR: sourcey:foo/1;2: Predicate: abc/0 is not defined (source apparently available at: pack(b_real); not asked to load)

?- defined( b_real/0, pack(b_real), [as_pack_err(true),load(library(b_real))] ).
true.

The above only succeeds if b_real is an install library and defines b_real/0.

From or Load can have the special form: lib(CodeLib). This assumes pack(lib) is installed and lib/1 will be used to load the requested CodeLib.

?- defined( b_real/0, lib(b_real), load(true) ),

Will again, only succeed if b_real is installed and defines b_real/0. In this occasion library(lib) should be also installed.

author
- nicos angelopoulos
version
- 0.1 2018/1/5
See also
- throw/2
- lib/1 (lib(suggests/1)) can work with this predicate
- lib/1 (lib(suggests/2)) as an alternative
 pack_errors
This is a documentation predicate, providing an anchor for documentation pointers.
 pack_errors_version(-Version, -Date)
Current version and release date for the library.
?- pack_errors_version( 0:3:0, date(2017,3,6) ).

Undocumented predicates

The following predicates are exported, but not or incorrectly documented.

 defined(Arg1, Arg2)