:- dynamic p/1 as incremental, etc

This is part of the XSB integration. XSB has a general predicate declaration scheme based on the syntax

:- prop p/1, ... as prop2, prop3, ...

That is different from the Quintus tradition that has directives for each property. As a result we get rather ugly definitions such as

:- multifile p/1, q/1.
:- dynamic p/1, q/1.

and similar combinations, notably involving volatile, multifile, dynamic, thread_local, etc.

In general I think we should aim to minimise declarations and leave decisions to the system. Nevertheless, SWI-Prolog will inherit a few more from XSB that are practically unavoidable. One of them is incremental that will declare networks of dynamic and tabled predicates for which the system will maintain consistency of tables under changes to the dynamic predicates. I think I would also be very happy with e.g.

:- dynamic p/1, q/1 as volatile.

I’m tempted to go with the XSB as based annotation. A but BUT is that as is currently an operator below the comma to support the module extensions agreed upon with YAP:

:- use_module(m, [ p/1 as mp, q/1 as mq, r/1]).

Going completely the XSB way requires to as to be op(1045, xfx, as). This causes syntax errors in programs that use as in places that do not allow for high priority operators. That will also break the above, requiring to write

:- use_module(m, [ (p/1 as mp), (q/1 as mq), r/1]).

An alternative is to define these operators as fy rather than fx, so we get something like below. I think that is quite acceptable.

:- op(1150, fy, dynamic).
:- op(1150, fy, volatile).

:- dynamic volatile p/1, q/1.

The downside of this is that we must declare e.g., incremental as such an operator and we cannot have properties with arguments. XSB uses that for e.g., index(Spec) and abstract(Level). We do not need indexing specifications, but we probably do need to the abstract(Level) property (please forget about the why for this discussion, just assume we may need some properties with arguments).

What is your opinion on this?

Logtalk also uses as/2 as an operator for declaring predicate (and non-terminal) aliases with the same definition as SWI-Prolog and YAP:

:- op(700, xfx, as).

How important is it to be able to run XSB programs without change? Probably not very important …

That is indeed not the main target. Considering the radically different module design this is pretty much infeasible. The aim of this project is to learn from each other and make those features that are fundamental to many applications targeted at one system available in the other. At the moment that concentrated on getting the fundamental aspects of XSB’s nifty tabling to SWI. Also here the syntax and API is not always the same, but it does provide stuff such as well founded semantics and hopefully shortly incremental tabling.

Somewhat more compatibility is achieved through the dialect framework established with YAP and now being developed for XSB. It allows loading quite a few XSB .P files unmodified in SWI.

Nevertheless, XSB has good ideas, in this case for setting multiple properties on multiple predicates. If XSB’s syntax looks good to us we should simply copy it. If not, we can invent something else and possibly use the XSB compatibility layer to provide maximal compatibility.

Do you have any clue whether this is used at some scale? If it is barely used (my impression) it might be ok to change the operator. Existing code will raise a syntax error and is easy to fix, both locally and by changing the operator definition to load an old program.

Note that I’m not too happy turning rather common words into operators with a priority above the coma as it always raises syntax errors. We’ve seen that with table last time.

Aliases are fairly used, specially when programming at scale.

Not looking forward to fix new sources of syntax errors, specially in code that was always portable, by adding ()'s where ()'s were never required before.

Ok. Something to take into account. Of course, it is up to Logtalk to (re)define operators. Note that the as construct is already used for tabling, but requires you to write

:- table (p/1, q/2) as subsumptive.

Forcing parenthesis might make sense. The above still looks reasonable to me while the XSB compatibility layer can simply redefine the operator. The below looks a little less though:

:- dynamic (p/1, q/2) as (incremental, abstract(0)).

Possibly still good enough though. Other suggestions are welcome. I need to take a decision soon …

More conventional might be

:- dynamic([ p/1, q/2], [incremental(true), abstract(0)]).
1 Like

I like the last option,

mostly because it doesn’t make another word an operator. Like Jan said above,

and indeed, I had a very confusing time writing some code a little while ago that had an atom table somewhere that caused syntax errors. Presumably, if one really wants the XSB-style as, one could write a term_expansion to convert the “more conventional” form into the other?

I also feel like this form makes it more clear that incremental & abstract are options to dynamic – :- dynamic (p/1, q/2) as (incremental, abstract(0)), for instance, makes me think that p is being aliased to incremental and q to abstract.

2 Likes

Thanks :slight_smile: I guess I’ already too much influenced by working with XSB :slight_smile:

In my internal branch (because the code is incomplete) I now have dynamic/2:

                                                 Availability: built-in

dynamic(:ListOfPredicateIndicators, +Options)
    As dynamic/1, but  allows for  setting additional  properties. This
    predicate  allows  for  setting  multiple  properties  on  multiple
    predicates. Defined Options are:

    incremental(+Boolean)
        Make the dynamic predicate signal depending tables. See section
        7.7.
    thread(+Local)
        Local  is  one  of   shared  (default)   or  local.   See  also
        thread_local/1.
    multifile(+Boolean)
    discontiguous(+Boolean)
    volatile(+Boolean)
        Set    the    corresponding    property.    See    multifile/1,
        discontiguous/1 and volatile/1.

I’m wondering though whether it would not be a better idea to add

set_predicate_properties(ListOfPreds, ListOfProperties).

And support all traditional properties.

I don’t think something like dynamic/2 is a good solution. I don’t see any sensible reasoning to prefer e.g. dynamic/2 + multifile property over multifile/2 + dynamic property. It also doesn’t give us consistent declaration of predicate properties. Notably, a meta-predicate declaration uses a template instead of a predicate indicator.

Meta-predicate cannot be part of this, but that is fine. It is unlikely that you want a whole bunch of predicates to have the same meta-predicate signature.

The other predicates you want to set are boolean, so there is no problem. A generic thing to set multiple properties on multiple predicates is nice, although most seem to combine with dynamic. Notably volatile, thread local/shared and the incremental tabling properties. Multifile is also common if it concerns some hook that can also be set dynamically.

So, I still think dynamic/2 makes sense. I’m also still open for alternatives, but being able to set multiple properties on multiple predicates is a requirement given the number of properties we have now.

Why having incremental(+Boolean), multifile(+Boolean), discontiguous(+Boolean), and volatile(+Boolean) instead of simply incremental, multifile, discontiguous, and volatile? If absent from the list of options, those properties would be assumed false.

For consistency of option lists. AFAIK, all ISO options take a single value and that is also true for SWI-Prolog, except for some old stuff that takes zero more more than one option value. library(option) assumes a single argument.

I agree it is a bit ugly here. Consistency is a strong motivation though :slight_smile: