% This LaTeX document was generated using the LaTeX backend of PlDoc,
% The SWI-Prolog documentation system



\subsection{The strength and weakness of predicate options}

\label{sec:predopts-pro-cons}

Many ISO predicates accept options, e.g., \predref{open}{4}, \predref{write_term}{3}. Options
offer an attractive alternative to proliferation into many predicates
and using high-arity predicates. Properly defined and used, they also
form a mechanism for extending the API of both system and application
predicates without breaking portability. I.e., previously fixed
behaviour can be replaced by dynamic behaviour controlled by an option
where the default is the previously defined fixed behaviour. The
alternative to using options is to add an additional argument and
maintain the previous definition. While a series of predicates with
increasing arity is adequate for a small number of additional
parameters, the untyped positional argument handling of Prolog quickly
makes this unmanageable.

The ISO standard uses the extensibility offered by options by allowing
implementations to extend the set of accepted options. While options
form a perfect solution to maintain backward portability in a linear
development model, it is not well equipped to deal with concurrent
branches because

\begin{enumerate}
    \item There is no API to find which options are supported in a
particular implementation.
    \item While the portability problem caused by a missing predicate in
Prolog \textit{A} can easily be solved by implementing this predicate, it
is much harder to add processing of an additional option to an
already existing predicate.
\end{enumerate}

Different Prolog implementations can be seen as concurrent development
branches of the Prolog language. Different sets of supported options
pose a serious portability issue. Using an option \textit{O} that establishes
the desired behaviour on system \textit{A} leads (on most systems) to an error
or system \textit{B}. Porting may require several actions:

\begin{itemize}
    \item Drop \textit{O} (if the option is not vital, such as the layout options
to \predref{write_term}{3})
    \item Replace \textit{O} by \textit{O2} (i.e., a differently named option doing the
same)
    \item Something else (cannot be ported; requires a totally
different approach, etc.)
\end{itemize}

Predicates that process options are particularly a problem when writing
a compatibility layer to run programs developed for System \textit{A} on System
\textit{B} because complete emulation is often hard, may cause a serious
slowdown and is often not needed because the application-to-be-ported
only uses options that are shared by all target Prolog implementations.
Unfortunately, the consequences of a partial emulation cannot be
assessed by tools.

\subsection{Options as arguments or environment?}

\label{sec:predopts-environment}

We distinguish two views on options. One is to see them as additional
parameters that require strict existence, type and domain-checking and
the other is to consider them `locally scoped environment variables'.
Most systems adopt the first option. SWI-Prolog adopts the second: it
silently ignores options that are not supported but does type and domain
checking of option-values. The `environment' view is commonly used in
applications to create predicates supporting more options using the
skeleton below. This way of programming requires that \textit{pred1} and
\textit{pred2} do not interpret the same option differently. In cases where
this is not true, the options must be distributed by \textit{some_pred}. We
have been using this programming style for many years and in practice it
turns out that the need for active distribution of options is rare.
I.e., options either have distinct names or multiple predicates
implement the same option but this has the desired effect. An example of
the latter is the \const{encoding} option, which typically needs to be applied
consistently.

\begin{code}
some_pred(..., Options) :-
      pred1(..., Options),
      pred2(..., Options).
\end{code}

As stated before, options provide a readable alternative to high-arity
predicates and offer a robust mechanism to evolve the API, but at the
cost of some runtime overhead and weaker consistency checking, both at
compiletime and runtime. From our experience, the `environment' approach
is productive, but the consequence is that mistyped options are silently
ignored. The option infrastructure described in this section tries to
remedy these problems.

\subsection{Improving on the current situation}

\label{sec:predopts-improving}

Whether we see options as arguments or locally scoped environment
variables, the most obvious way to improve on the current situation is
to provide reflective support for options: discover that an argument is
an option-list and find what options are supported. Reflective access to
options can be used by the compiler and development environment as well
as by the runtime system to warn or throw errors.

\subsubsection{Options as types}

\label{sec:predopts-as-types}

An obvious approach to deal with options is to define the different
possible option values as a type and type the argument that processes
the option as list($<$option_type$>$), as illustrated below. Considering
options as types fully covers the case where we consider options as
additional parameters.

\begin{code}
:- type open_option ---> type(stream_type) |
                         alias(atom) | ... .
:- pred open(source_sink, open_mode, stream, list(open_option)).
\end{code}

There are three reasons for considering a different approach:

\begin{itemize}
    \item There is no consensus about types in the Prolog world, neither about
what types should look like, nor whether or not they are desirable. It
is not likely that this debate will be resolved shortly.
    \item Considering options as types does not support the `environment'
view, which we consider the most productive.
    \item Even when using types, we need reflective access to what
options are provided in order to be able to write
compile or runtime conditional code.
\end{itemize}

\subsubsection{Reflective access to options}

\label{sec:predopts-reflextion}

From the above, we conclude that we require reflective access to find
out whether an option is supported and valid for a particular predicate.
Possible option values must be described by types. Due to lack of a type
system, we use \file{library(error)} to describe allowed option values.
Predicate options are declared using \predref{predicate_options}{3}:

\begin{description}
    \predicate[det]{predicate_options}{3}{:PI, +Arg, +Options}
Declare that the predicate \arg{PI} processes options on \arg{Arg}. \arg{Options}
is a list of options processed. Each element is one of:

\begin{itemize}
    \item Option(ModeAndType)
\arg{PI} processes Option. The option-value must comply to
ModeAndType. Mode is one of + or - and Type is a type as
accepted by \predref{must_be}{2}.
    \item pass_to(:\arg{PI},\arg{Arg})
The option-list is passed to the indicated predicate.
\end{itemize}

Below is an example that processes the option \verb$header(boolean)$
and passes all options to \predref{open}{4}:

\begin{code}
:- predicate_options(write_xml_file/3, 3,
                     [ header(boolean),
                       pass_to(open/4, 4)
                     ]).

write_xml_file(File, XMLTerm, Options) :-
    open(File, write, Out, Options),
    (   option(header(true), Options, true)
    ->  write_xml_header(Out)
    ;   true
    ),
    ...
\end{code}

This predicate may only be used as a \textit{directive} and is
processed by \predref{expand_term}{2}. Option processing can be
specified at runtime using \predref{assert_predicate_options}{3}, which is
intended to support program analysis.

    \predicate[semidet]{assert_predicate_options}{4}{:PI, +Arg, +Options, ?New}
As predicate_options(:\arg{PI}, +\arg{Arg}, +\arg{Options}). \arg{New} is a boolean
indicating whether the declarations have changed. If \arg{New} is
provided and \const{false}, the predicate becomes semidet and fails
without modifications if modifications are required.
\end{description}

The predicates below realise the support for compile and runtime
checking for supported options.

\begin{description}
    \predicate[nondet]{current_predicate_option}{3}{:PI, ?Arg, ?Option}
True when \arg{Arg} of \arg{PI} processes \arg{Option}. For example, the following
is true:

\begin{code}
?- current_predicate_option(open/4, 4, type(text)).
true.
\end{code}

This predicate is intended to support conditional compilation
using \predref{if}{1} ... \predref{endif}{0}. The predicate
\predref{current_predicate_options}{3} can be used to access the full
capabilities of a predicate.

    \predicate[det]{check_predicate_option}{3}{:PI, +Arg, +Option}
Verify predicate options at runtime. Similar to
\predref{current_predicate_option}{3}, but intended to support runtime
checking.

\begin{tags}
\mtag{Errors}- \verb$existence_error(option, OptionName)$ if the option is not
supported by \arg{PI}. \\- \verb$type_error(Type, Value)$ if the option is supported but
the value does not match the option type. See \predref{must_be}{2}.
\end{tags}
\end{description}

The predicates below can be used in a development environment to inform
the user about supported options. PceEmacs uses this for colouring
option names and values.

\begin{description}
    \predicate[nondet]{current_option_arg}{2}{:PI, ?Arg}
True when \arg{Arg} of \arg{PI} processes predicate options. Which options
are processed can be accessed using \predref{current_predicate_option}{3}.

    \predicate[nondet]{current_predicate_options}{3}{:PI, ?Arg, ?Options}
True when \arg{Options} is the current active option declaration for
\arg{PI} on \arg{Arg}. See \predref{predicate_options}{3} for the argument
descriptions. If \arg{PI} is ground and refers to an undefined
predicate, the autoloader is used to obtain a definition of the
predicate.
\end{description}

The library can execute a complete check of your program using
\predref{check_predicate_options}{0}:

\begin{description}
    \predicate[det]{check_predicate_options}{0}{}
Analyse loaded program for erroneous options. This predicate
decompiles the current program and searches for calls to
predicates that process options. For each option list, it
validates whether the provided options are supported and
validates the argument type. This predicate performs partial
dataflow analysis to track option-lists inside a clause.

\begin{tags}
    \tag{See also}
\predref{derive_predicate_options}{0} can be used to derive
declarations for predicates that pass options. This
predicate should normally be called before
\predref{check_predicate_options}{0}.
\end{tags}
\end{description}

The library offers predicates that may be used to create declarations
for your application. These predicates are designed to cooperate with
the module system.

\begin{description}
    \predicate[det]{derive_predicate_options}{0}{}
Derive new predicate option declarations. This predicate
analyses the loaded program to find clauses that process options
using one of the predicates from \file{library(option)} or passes
options to other predicates that are known to process options.
The process is repeated until no new declarations are retrieved.

\begin{tags}
    \tag{See also}
\predref{autoload}{0} may be used to complete the loaded program.
\end{tags}

    \predicate[det]{retractall_predicate_options}{0}{}
Remove all dynamically (derived) predicate options.

    \predicate[nondet]{derived_predicate_options}{3}{:PI, ?Arg, ?Options}
Derive option arguments using static analysis. True when \arg{Options}
is the current \textit{derived} active option declaration for \arg{PI} on
\arg{Arg}.

    \predicate[det]{derived_predicate_options}{1}{+Module}
Derive predicate option declarations for a module. The derived
options are printed to the \verb$current_output$ stream.
\end{description}

