Assertz/1 with attributed variables

I’m using: SWI-Prolog version 9.2.9

I want to assert a fact containing an attributed variable.

But what I’m getting is: The attribute is lost.

My code looks like this:

?- put_attr(V, typing, t1), assertz(tv(V)), tv(A), get_attr(A, typing, T).
false.

It work fine (unsurprisingly), if I directly check the attribute:

?- put_attr(V, typing, t1), get_attr(V, typing, T).
T = t1,
put_attr(V, typing, t1).

The following also works, but note how it fails to set the attribute for A:

?- put_attr(V, typing, t1), assertz(tv(V)), tv(A).
put_attr(V, typing, t1).

I believe this behaviour is by design, despite being disappointing when experienced.

I think the usual workarounds are:

  • Resolve, i.e effectively remove the attributed variables before asserting
  • Use some other method, using terms/lists to represent the outstanding constraints
1 Like

Compiled clauses do not support attributes at the moment. There are some ways out:

  • Use the recorded db (recorda/2, etc.)
  • Use global variables (b_setval/2 and friends)
  • Extract the attributes and restore them later. You can do that using copy_term/3. This is a robust but bit slow approach. The other is to merely use term_attvars/2 and get_attrs/2.
1 Like

Another solution that worked in my case is passing all terms explicitly instead of relying on assertz, i.e., pass a list of facts that I asserted before.

i keep expecting at some point that someone would’ve made a library to let you use asserta/z, clause/2/3, retract/all, etc., in a way that pretends the database supports attributed variables. one approach i’ve halfway done in the past is to actually keep the attribute goals in the body of the asserted clause, instead of trying to reconstruct attributes at query time.

for example, you might write:

tr(V, typing, t1), assertz_attvar((tv(V) :- hello(V))).

**
Behind the scenes, the library would use copy_term/3 to pull out any attribute goals from V before storing the clause.** That way, what actually gets stored is:

tv(A) :- put_attr(A, typing, t1), hello(A).

so when you later call tv(X), the attribute gets restored automatically for that variable before the rest of your logic (hello(X)) runs.

the library would provide alternate versions of all the standard dynamic predicates, each with _attvar appended (for “attributed variable”). so you’d have:

  • assertz_attvar/1,2

  • asserta_attvar/1,2

  • assert_attvar/1,2

  • retract_attvar/1

  • retractall_attvar/1

  • clause_attvar/2

  • clause_attvar/3

maybe even a wrapper like with_attvars_in_assert_db/1 that could intercept such calls within a given scope, so you wouldn’t have to change every predicate in your codebase.

of course, the open question is whether restoring the attribute goals in this way (by prepending them to the body) will always match what users actually expect—especially regarding when attribute hooks get triggered during unification. it’s just one more thing that has to be answered: will the semantics really line up with what the library’s user wanted?

i’ve put together pieces of this a couple times, but i’ve never seen a polished, general-purpose library for it. seems like something the community should have by now!

You are the community. What’s stopping you?

1 Like