Using term_expansion to introduce constructed goals

I’m trying to come up with a way to wrap a common pattern of use of CHR using term_expansion. Unfortunately I’ve only ever VERY lightly dabbled in term_expansion (largely because I think of it as pretty evil) so I’ve never done anything particularly sophisticated in it. This is biting me hard today.

This is the code I’m trying to make:

:- use_module(library(chr)).

term_expansion(T,
    [
         (:- chr_constraint Constraint/1)
        ,(:- chr_constraint TempConstraint/1)
        ,(:- chr_constraint CollectConstraint/1)
        ,(:- chr_constraint GetConstraint/1)
        ,(?- Constraint(_C1), GetConstraint(_) ==> TempConstraint(_C1))
    ]) :-
    nonvar(T),
    T = collectible(Constraint),
    atom(Constraint),
    atomic_list_concat([Constraint, temp], '_', TempConstraint),
    atomic_list_concat([Constraint, collect], '_', CollectConstraint),
    atomic_list_concat([Constraint, get], '_', GetConstraint)
    .

collectible(foo).

The idea is to take a name (later a name/arity, but that’s out of scope for this at the moment) and declare four constraints from it. So in the sample there, the base name is foo and the expanded code declares the four constraints foo/1, foo_temp/1, foo_collect/1, and foo_get/1. This part works. All four constraints are properly declared and show up in the listing with the expected content.

What’s NOT working is the next line. I want it to insert the following clause into the source:

foo(_C1), foo_get(_) ==> foo_temp(_C1).

Instead I get a syntax error (operator expected) after the “ai” in “Constraint”.

I’ve tried various formulations of this, including ditching the ?-, using :- in place, etc. all just to see if there’s a different error message at least. There isn’t. If I use a variable there at all it fails. If, however, I manually make it this:

,(?- foo(_C1), foo_get(_) ==> foo_temp(_C1))

It again works fine. This leads me to believe that it’s freaking out over the presence of a variable there. Which baffles me because I use variables above in the chr_constraint declarations and it’s all hunky-dory with those, declaring appropriate predicates behind the scenes to interface with CHR.

So what am I missing? How can I build that

,(?- foo(_C1), foo_get(_) ==> foo_temp(_C1))

But from the variables giving me names instead of atoms?

Two things: Firstly, I don’t think you need the ?- for the last clause.
The main issue though is that you can’t write a functor with a variable as the “head”; you have to build the the term “manually”.

The below loads for me:

:- use_module(library(chr)).

term_expansion(T,
    [
         (:- chr_constraint Constraint/1)
        ,(:- chr_constraint TempConstraint/1)
        ,(:- chr_constraint CollectConstraint/1)
        ,(:- chr_constraint GetConstraint/1)
        ,(ConstraintC1, CallGetConstraint ==> TempConstraintC1)
    ]) :-
    nonvar(T),
    T = collectible(Constraint),
    atom(Constraint),
    atomic_list_concat([Constraint, temp], '_', TempConstraint),
    atomic_list_concat([Constraint, collect], '_', CollectConstraint),
    atomic_list_concat([Constraint, get], '_', GetConstraint),
    ConstraintC1 =.. [Constraint, C1],
    CallGetConstraint =.. [GetConstraint, _],
    TempConstraintC1 =.. [TempConstraint, C1].

collectible(foo).
1 Like

The ?- thing came from the docs, but … ah … the docs thing is a major issue in the darker corners of SWI. (Re-reading the part that got me there leaves at least three readings.)

The irony of the solution you provide (for which thanks!) is that after I solved the /1 case I was going to use univ to solve the /n case. So to have univ be the solution is heartening, as well as a bit face-palming at my own stupidity. :smiley:

1 Like

The problem with docs is similar to comments: they tend not to follow changes. The doc of the changed predicate is often updated, but if the same thing is touched in other places it is often not :frowning:

Currently, there is no difference between :- and ?- used in sources. AFAIK, ?- is non-standard and new code should (thus) use :-.

If the docs say something different, please point out where! Ideally sent a pull request :slight_smile: