Prolog flags in libraries and packages

While the standard Prolog flags are identified in Environment Control (Prolog flags), external code such as libraries and packages can also make use of Prolog flags.

In reading load_html/3 noticed

Calls load_structure/3 with the given Options, using the default options dialect(HTMLDialect) , where HTMLDialect is html4 or html5 (default), depending on the Prolog flag html_dialect .

Checking the value after the library is loaded

?- current_prolog_flag(html_dialect,Dialect).
Dialect = html5.

Checking the value before the library is loaded

?- current_prolog_flag(html_dialect,Dialect).
false.

An error (non-existing flag) would be best in this case instead of failure, which will easily mask bugs (e.g. testing if a flag have a given value when the failure is caused by the flag not existing instead of the flag having a different value).

P.S. The ISO Prolog standard requires a domain error when a flag doesn’t exist.

1 Like

Which is, IMO, a very unfortunate choice. Prolog systems vary wildly in terms of defined flags and the most common test it where you want something specific if a flag is defined and has some value. Also the name is misleading. All current_* enumerate existing objects and fail silently on non-existence. In portable code you now have to do

catch(current_prolog_flag(html_dialect, Dialect),
      error(existence_error(prolog_flag, _), _),
      fail).

Note that using _ as n error is unwise. You may have misspelled current_prolog_flag/2 or run into a resource error because the unification causes a stack overflow.

The above is unnecessary slow (on most Prolog systems), verbose and harms debugging, for example if you tell the system to trace in case of an exception. You should not need to rely on exceptions for normal work. One of the few exceptions to this rule is code that relies on things that may asynchronously change and thus a test for the status may be incorrect right after the test completes.

I would not be against a predicate prolog_flag(+Flag, -Value) that would raise an existing value and which you can use for the cases that you expect the flag to be present and want to know its value.

2 Likes

In checking the ISO Prolog spec


7.11 Flags

A flag is an atom which is associated with a value that is either implementation defined or defined by the user.

Each flag has a permitted range of values; any other value is a Domain Error (7.12.2 c). The range of values associated with some flags can be extended with additional implementation specific values.

Correct me if I am wrong, but the way I read that it is only a domain error when setting the value. If the flag does not exist because it is from a library, then returning false is what I would expect.


However in further checking the use of the flag html_dialect after the library is loaded

?- current_prolog_flag(html_dialect,Dialect).
Dialect = html5.

?- set_prolog_flag(html_dialect,foo).
true.

?- current_prolog_flag(html_dialect,Dialect).
Dialect = foo.

I would expect the domain error when setting the value to foo.

The root issue, in my view, is that current_prolog_flag/2 is not a replacement for a missing current_prolog_flag/1 predicate, which would be a more sensible comparison to the semantics of e.g. current_predidate/1.

With a current_prolog_flag/1 predicate with simple semantics (succeeds if a flag exists and fails otherwise), there would be much less of an issue current_prolog_flag/2 throwing an exception on a non-existing flag.

The ISO Prolog Core standard specification for the current_prolog_flag/2 predicate dictates a type error when the flag is neither a variable or an atom and a domain error when the flag is invalid.

OK, now I see what you are referring to:

A.5.17.2 current-prolog_flag/2

Error cases:

in-error(-, func(current-prolog-flag, Flag.Value.nil),
   type-error(atom,Flag)) <=
   not L-var(Flag),
   not D-is-an-atom(Flag).

in-error(-, func(current-prolog-flag, Flag.Value.nil),
   domain-error(prolog_flag,Flag)) <=
   D-is-an-atom(Flag),
   not D-is-a-flag(Flag).

That would help a lot. Still, AFAIK, no current_* predicate raises an error if the target object doesn’t exist while the instantiation refers to a specific instance. Multi-argument current_* are not new, e.g., current_op/3 which, AFAIK, raises no error if the operator doesn’t exist. Somehow the ISO standard sees flags not as things/instances/…, but states there is a domain of defined flags (they do also not allow for defining new flags).

Being able to define new flags simplifies portability issues, i.e., it allows defining a flag that the original implementation has and that is used by the application. It also avoids the need for libraries and applications to introduce something that is typically very similar to flags. Finally, for SWI-Prolog they provide an important need for relating settings with threads, where new threads copy the flags from their creator but subsequent flag changes have only local effect (in fact, this is implemented using copy-on-write).

No. The only errors (in the standard specification) are if the arguments are not valid. Calling e.g. current_op(A, B, foo42) simply fails.

Although, as discussed here in this thread today, there are reasons that justify both current_prolog_flag/2 alternatives of failing or throwing an error on an invalid flag, its specification (in the standard) to throw an error is not consistent, as you pointed out, with the semantics of the other current_* predicates.

1 Like

Unfortunately, the ISO standard is set in stone :frowning:

rip

2 Likes