options.pl -- Options handling and processing.

This is a stoics.infrastructure pack for handling option arguments. The main concept is to treat options as naive Prolog lists which the programmer can manipulate and specialise if they need to, while providing a small number of predicates that manage basic common operations on options. Options are particularly important in the context of SWI packs, as making code publicly available to others often involves allowing for variations in the behaviour of the code.

The library provides simple extensions to the basic list manipulation predicates. In many cases it is just the error handling that is the main difference to standard predicates.

Technically the library is designed on the semantics of memberchk/2. Looking for an Option in a list of options, memberchk/2 will return the leftmost match. Library(options) sees options as a concatenation (append/3) of the user provided options (arguments for hereon) and the defaults provided by the predicate.

The default option values for a predicate are given by a predicate of the same name but postfixed by '_defaults'. The library also allows for reading user specific default options by reading profiles from a file located at $HOME/.pl/<pred_name>.pl, if that file exists. Each options file should have a number of option terms given as facts.

Some distinctive features of pack(options)

For an example see program options_example_sort_defaults.pl in examples directory.

?- edit( pack(options/examples/ex_sort) ).

?- [pack(options/examples/ex_sort)].

?- ex_sort( [a,b,e,c,b], Ord, true ).
Ord = [a, b, c, e].
?- ex_sort( [a,b,e,c,b], Ord, debug(true) ).
% Input list length: 5
% Output list length: 4
Ord = [a, b, c, e].
?- ex_sort( [a,b,e,c,b], Ord, order(>) ).
Ord = [e, c, b, a].
?- ex_sort( [a,b,e,c,b], Ord, duplicates(true) ).
Ord = [a, b, b, c, e].

Create file $HOME/.pl/ex_sort.pl with content order(>).

?- ex_sort( [a,b,e,c,b], Ord, true ).
Ord = [e, c, b, a].

Default for user is now order(>) which can still be over-ridden at invocation

?- ex_sort( [a,b,e,c,b], Ord, order(<) ).
Ord = [a, b, c, e].

Predicates

author
- nicos angelopoulos
version
- 0.2.0 2015/7/5
- 0.4.0 2016/2/29
To be done
- type checking (see options_propagate for a first use)
options_version(-Version, -Date)
Current version and release date for the library.
options(+Required, +Opts)
options(+Required, +Opts, +OptionsOpts)
This should more naturally be option/2 but as this might cause confusion with Swi's own option library. Required can be a single or list of terms.

Opts can also be non list as it is passed through en_list/2.

OptionsOpt

en_list(Enlist=false)
when true it enlists the returning argument
ground(Ground=false)
should the selected option required to be ground, and what to do if not
false
dont do anything
true
fail
error
throw error
true(TruthValue)
only variable is allowed, if present the call always succeeds and TruthValue is bound to either true or false.
rem_opts(RemOpts)
RemOpts is Opts after all functor/shape matching mentions of Required have been removed.

The predicate fails silently if the first Required option with equivalent "shape" in Opts fails to pattern match (unlike classic memberchk/2).

 ?- options( x(y), [x(x)] ).
 false.
 
 ?- options( x(y), [x(x),x(y)] ).
 false.

 ?- options( x(X), [a(b),c(d),x(y),x(x)] ).
 X=y.

 ?- options( [a(X),c(b)], [a(b),x(x),b(c)] ).
 ERROR: Required option: c(b), not present in options: [a(b),x(x)]
 % Execution Aborted
 
 ?- options( x(X), [a(b),c(d),x(y),x(x)], rem_opts(Rem) ).
 X = y,
 Rem = [a(b), c(d)].

 ?- options( x(X), [a(b),c(d),x(y),x(x)], en_list(true) ).
 X= [y].

 ?- options( a(A), [a(X),b(c)], ground(error) ).
 ERROR: pack(options): Option should be ground, but found: a(_G1470), in options: [a(_G1470),b(c)]
 ?- options( a(A), [a(X),b(c)], ground(true) ).
 false.
 
 ?- options( a(A), [a(X),b(c)] ).
 A = X.
 
 ?- options( a(A), [a(X),b(c)], ground(false) ).
 A = X.
 
 ?- options( a(b), [a(a),a(b),b(c)], true(T) ).
 T = fail.
 
 ?- options( a(A), [a(a),a(b),b(c)], true(T) ).
 A = a,
 T = true.
 
 ?- options( a(a), [a(a),a(b),b(c)], true(T) ).
 T = true.
 
author
- nicos angelopoulos
version
- 0.2 2015/01/16
- 0.3 2015/12/06 changed 3rd argument to Options of its own invocation
en_list(+Term, -Listed)
Ensure that Term is either a list of things or a non-var term that that is wrapped to a singleton list. If In is a variable a ball is thrown.
 ?- en_list( x(y), Opts ).
 Opts = [x(y)].

 ?- en_list( [x,y], Opts ).
 Opts = [x, y].

 ?- en_list( X, Opts ).
 ERROR: Unhandled exception: en_list(encoutered_variable_in_1st_arg(_))
options_append(+PredName, +OptS, -All)
options_append(+PredName, +OptS, -All, +OAopts)
Look for PredName_defaults/1 and if that exists append it to OptS to get All. OptS is casted to a list before the append, so single terms are allowed as options. Listens to debug(options_append).

The predicate can process debug(Dbg) a commonly used option. Default should be provided by PredName caller. The infrastructure allows for other options to be added easily.

OAopts term or list of:

extra_arg(Arg)
multiple allowed. All Arg(s) so passed are added to the Args passed to the defaults predicate, but not to the generated options. Allows for instance to pass argments of the call itself to the defaults predicate without those arguments being added to the Options list
foreign(Foreign)
instantiates to all options that do not have matching default term structure
debug(Dbg=none)
if true debug this call, and call prolog_debug:debug_topic(Pname). if false trun debugging off for this call. Else Dbg can be a debug term (other than none,false,true) or list of debug terms.
funnel(Proccess)
as process() below, but leaves processed options in All.
process(Proccess)
with Proccess in
  • debug will turn on debugging according to debug/0,1,2 options, see below

When processing debugging options in All, the first matching term of the following is used:

debug
short for debug(true)
debug(Dbgs)
short for debug(Dbgs,_Prior)
debug(Dbgs, Prior)
Prior is the prior status of debug(PredName). For each element of Dbgs call RHS:
true
debug(PredName)
false
nodebug(PredName)
none
true
all
debug(_)
Other
debug(Other)
?- assert( demo_defaults(ls(true)) ).
?- options_append( demo, ls(false), All ).
All = [ls(false), ls(true)].
 
?- options_append( demo, debug, All, process(debug) ).
All = [ls(true)].
 
?- options_append( demo, [debug(true),ls(false)], All, [process(debug),debug(true)] ).
Turning debugging on for predicate handle: demo
All = [ls(false), ls(true)].
 
% Note that the debug(options_append) has been removed.
?- options_append( demo, debug, All, process(debug) ).
All = [ls(true)].

The default OAopts list is [funnel(debug)].

author
- nicos angelopoulos
version
- 0.2 2014/9/20
See also
- ~/bin/cline/keep.pl for debug option example
To be done
- add option! for making sure that only recognised options (with additional,"silent" defaults) are accepted ??
- allow for strict lists inputs
options_debug(+Format, +Args, +Opts)
Call debug(_,Format,Args) iff debug(true) is the first debug(_) term in list Opts
 ?- options_debug( 'A simple message at: ~w', noon, [debug(true)] ).
 ?- options_debug( 'A simple message at: ~w', noon, [] ).
author
- nicos angelopoulos
version
- 0.1 2015/1/15
options_propagate(+Onames, +OptsIn, -OptsOut, +PropOpts)
Propagate all options with names in Onames from OptsIn to OptsOut. Onames can be a sinle un_listed option name (en_list/2).

The predicate does not check arities.

The order in OptsOut follows the order in Onames.

PropOpts control the predicate behaviour

match(Match=first)
duplicates are removed, use match(all) for no removal
author
- nicos angelopoulos
version
- 0.1 2015/3/19
To be done
- type checking of Match via options_append/4.
options_restore(+Self, +Opts)
Restore all options_append/3, auto processed options.

This predicate is often called at the end of a deterministic call that depends on a set of options.

Currently the only option that is restored is

Internals; The predicate looks for any macthing '$restore'(Self,OptName,Status), so it should be extensible to other state-based options processing.

options_return(+TermS, +OptS)
The predicate always succeeds. But if Opts memberchk/2 with Term then it would instantiate TermS (a non-list term or list of terms). The predicate, thus the name, can be used to return optional information to the caller.

OptS and TermS are passed through en_list/2.

 ?- options_return( cnm(Cnm), [abc(x),cnm(symbols)] ).
 Cnm = symbols.
 ?- options_return( [data(Dt),cnm(Cnm)], [abc(x),cnm(symbols)] ).
 Cnm = symbols
options_call(+Goal, +Opts)
Call Goal by extending it with a first argument that comes from selecting a homonymous option from Opts.
Opts call_module(Mod=user) the module in which to call extended Goal
?- options_call( plus(2,X), plus(1) ).
X = 3
author
- nicos angelopoulos
version
- 0.1 2015/12/15 @ allow for , separate goals ?
options(+Required, +Opts)
options(+Required, +Opts, +OptionsOpts)
This should more naturally be option/2 but as this might cause confusion with Swi's own option library. Required can be a single or list of terms.

Opts can also be non list as it is passed through en_list/2.

OptionsOpt

en_list(Enlist=false)
when true it enlists the returning argument
ground(Ground=false)
should the selected option required to be ground, and what to do if not
false
dont do anything
true
fail
error
throw error
true(TruthValue)
only variable is allowed, if present the call always succeeds and TruthValue is bound to either true or false.
rem_opts(RemOpts)
RemOpts is Opts after all functor/shape matching mentions of Required have been removed.

The predicate fails silently if the first Required option with equivalent "shape" in Opts fails to pattern match (unlike classic memberchk/2).

 ?- options( x(y), [x(x)] ).
 false.
 
 ?- options( x(y), [x(x),x(y)] ).
 false.

 ?- options( x(X), [a(b),c(d),x(y),x(x)] ).
 X=y.

 ?- options( [a(X),c(b)], [a(b),x(x),b(c)] ).
 ERROR: Required option: c(b), not present in options: [a(b),x(x)]
 % Execution Aborted
 
 ?- options( x(X), [a(b),c(d),x(y),x(x)], rem_opts(Rem) ).
 X = y,
 Rem = [a(b), c(d)].

 ?- options( x(X), [a(b),c(d),x(y),x(x)], en_list(true) ).
 X= [y].

 ?- options( a(A), [a(X),b(c)], ground(error) ).
 ERROR: pack(options): Option should be ground, but found: a(_G1470), in options: [a(_G1470),b(c)]
 ?- options( a(A), [a(X),b(c)], ground(true) ).
 false.
 
 ?- options( a(A), [a(X),b(c)] ).
 A = X.
 
 ?- options( a(A), [a(X),b(c)], ground(false) ).
 A = X.
 
 ?- options( a(b), [a(a),a(b),b(c)], true(T) ).
 T = fail.
 
 ?- options( a(A), [a(a),a(b),b(c)], true(T) ).
 A = a,
 T = true.
 
 ?- options( a(a), [a(a),a(b),b(c)], true(T) ).
 T = true.
 
author
- nicos angelopoulos
version
- 0.2 2015/01/16
- 0.3 2015/12/06 changed 3rd argument to Options of its own invocation
options_append(+PredName, +OptS, -All)
options_append(+PredName, +OptS, -All, +OAopts)
Look for PredName_defaults/1 and if that exists append it to OptS to get All. OptS is casted to a list before the append, so single terms are allowed as options. Listens to debug(options_append).

The predicate can process debug(Dbg) a commonly used option. Default should be provided by PredName caller. The infrastructure allows for other options to be added easily.

OAopts term or list of:

extra_arg(Arg)
multiple allowed. All Arg(s) so passed are added to the Args passed to the defaults predicate, but not to the generated options. Allows for instance to pass argments of the call itself to the defaults predicate without those arguments being added to the Options list
foreign(Foreign)
instantiates to all options that do not have matching default term structure
debug(Dbg=none)
if true debug this call, and call prolog_debug:debug_topic(Pname). if false trun debugging off for this call. Else Dbg can be a debug term (other than none,false,true) or list of debug terms.
funnel(Proccess)
as process() below, but leaves processed options in All.
process(Proccess)
with Proccess in
  • debug will turn on debugging according to debug/0,1,2 options, see below

When processing debugging options in All, the first matching term of the following is used:

debug
short for debug(true)
debug(Dbgs)
short for debug(Dbgs,_Prior)
debug(Dbgs, Prior)
Prior is the prior status of debug(PredName). For each element of Dbgs call RHS:
true
debug(PredName)
false
nodebug(PredName)
none
true
all
debug(_)
Other
debug(Other)
?- assert( demo_defaults(ls(true)) ).
?- options_append( demo, ls(false), All ).
All = [ls(false), ls(true)].
 
?- options_append( demo, debug, All, process(debug) ).
All = [ls(true)].
 
?- options_append( demo, [debug(true),ls(false)], All, [process(debug),debug(true)] ).
Turning debugging on for predicate handle: demo
All = [ls(false), ls(true)].
 
% Note that the debug(options_append) has been removed.
?- options_append( demo, debug, All, process(debug) ).
All = [ls(true)].

The default OAopts list is [funnel(debug)].

author
- nicos angelopoulos
version
- 0.2 2014/9/20
See also
- ~/bin/cline/keep.pl for debug option example
To be done
- add option! for making sure that only recognised options (with additional,"silent" defaults) are accepted ??
- allow for strict lists inputs