# Reasonable way to combine a retractall and assert operation?

**URL:** <https://swi-prolog.discourse.group/t/reasonable-way-to-combine-a-retractall-and-assert-operation/882>\
**Category:** Help!\
**Created:** [June 29, 2019, 1:40am UTC](https://swi-prolog.discourse.group/t/reasonable-way-to-combine-a-retractall-and-assert-operation/882 "2019-06-29T01:40:05Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![prodog](https://avatars.discourse-cdn.com/v4/letter/p/57b2e6/32.png) [@prodog](https://swi-prolog.discourse.group/u/prodog)\
**Post date:** [June 29, 2019, 1:40am UTC](https://swi-prolog.discourse.group/t/reasonable-way-to-combine-a-retractall-and-assert-operation/882/1 "2019-06-29T01:40:05Z")

</div>

I’m using: SWI-Prolog version 8.0.2.

I find myself writing the pattern below a lot, when I want to replace a singleton item in the Prolog database:

```prolog
retractall(some_term_with_three_args(_,_,_)),
assert(some_term_with_three_args(arg_1, arg_2, arg_3)).

```

So I created my own replace predicate that combines both:

```prolog
replace_singleton_term(Term) :-
    functor(Term, PredName, Arity),
    functor(RetractTemplate, PredName, Arity),
    retractall(RetractTemplate),
    assert(Term), !.

```

My concern is that there might be some unforeseen consequence in using `functor()` to create the template for removing all occurrences of any existing database element with the same predicate name as that in `Term` and the same arity. Is there anything about what I’m doing here that could cause some unforeseen problem I might not expect to happen? Any other comments?

---

<div class="post-metadata">

**Author:** ![swi](https://avatars.discourse-cdn.com/v4/letter/s/51bf81/32.png) [@swi](https://swi-prolog.discourse.group/u/swi)\
**Post date:** [June 29, 2019, 3:32am UTC](https://swi-prolog.discourse.group/t/reasonable-way-to-combine-a-retractall-and-assert-operation/882/2 "2019-06-29T03:32:56Z")

</div>

I find it usually more useful to write predicates like this:

```prolog
   replace_term_with_three_args(A1,A2,A3) :-
       retractall(some_term_with_three_args(_,_,_)),
       assert(some_term_with_three_args(A1, A2, A3)).

```

As the project moves on, I usually need to do something special when the term is replaced (e.g. collect a count or handle some especial exceptional condition). With individual replace predicates like above it is easy to add the special condition to the `replace_term_with_three_args/3` predicate, something that you would find hard to do with the generic replace.

---

<div class="post-metadata">

**Author:** ![jan](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/jan/32/4_2.png) [@jan](https://swi-prolog.discourse.group/u/jan)\
**Post date:** [June 29, 2019, 3:52pm UTC](https://swi-prolog.discourse.group/t/reasonable-way-to-combine-a-retractall-and-assert-operation/882/3 "2019-06-29T15:52:41Z")

</div>

I think your first solution is pretty much ok. I’ve been thinking about this problem before. We want something that does the above in an _atomic_ way. I’m still doubting how though. It is fairly trivial to solve this one clause predicate case, but possible we should seek a more general solution such as proper transaction support.

Note that this type of programming for dealing with state can be really slow. Consider:

```prolog
l(N) :-
    assert(n(N)),
    count_down.

count_down :-
    retract(n(N0)), 
    N1 is N0-1,
    asserta(n(N1)),
    ( N1 == 0
    -> true
    ; count_down
    ).

```

vs.

```prolog
count_down(N) :-
    N > 0,
    !,
    N2 is N - 1,
    count_down(N2).
count_down(0).

```

This gives me the results below. All except the SWISH are run on the same hardware (Intel i7-3770). SWISH runs on slower hardware while the large number of threads there cause clause garbage collection to happen much less frequently and be more expensive.

| test | SWI | SWISH | XSB | YAP |
| --- | --- | --- | --- | --- |
| l(1 000 000) | 0.961 | 81.211 | 5.528 | crash |
| count\_down(1 000 000) | 0.059 | 0.123 | 0.05 | 0.059 |

---

<div class="post-metadata">

**Author:** ![prodog](https://avatars.discourse-cdn.com/v4/letter/p/57b2e6/32.png) [@prodog](https://swi-prolog.discourse.group/u/prodog)\
**Post date:** [June 29, 2019, 4:03pm UTC](https://swi-prolog.discourse.group/t/reasonable-way-to-combine-a-retractall-and-assert-operation/882/4 "2019-06-29T16:03:30Z")

</div>

> [@jan](#):
>
> We want something that does the above in an _atomic_ way

Is the primary motivation for creating an atomic transaction to make it thread-safe, so that the **retractall + assert** pair doesn’t get _split_ by a thread switch?

---

<div class="post-metadata">

**Author:** ![jan](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/jan/32/4_2.png) [@jan](https://swi-prolog.discourse.group/u/jan)\
**Post date:** [June 29, 2019, 4:08pm UTC](https://swi-prolog.discourse.group/t/reasonable-way-to-combine-a-retractall-and-assert-operation/882/5 "2019-06-29T16:08:28Z")

</div>

More that another thread that wants to have the current value, but gets a failure. It is hard to work around. Using a mutex for both reading and writing works, but scales poorly. If a double answer is not a problem you can also do

```prolog
    clause(n(N0), true, Ref),
    N1 is N0-1,
    asserta(n(N1)),
    erase(Ref).

```

Now if the reader uses `once(n(N))` you get consistent results.

---

<div class="post-metadata">

**Author:** ![prodog](https://avatars.discourse-cdn.com/v4/letter/p/57b2e6/32.png) [@prodog](https://swi-prolog.discourse.group/u/prodog)\
**Post date:** [June 29, 2019, 6:03pm UTC](https://swi-prolog.discourse.group/t/reasonable-way-to-combine-a-retractall-and-assert-operation/882/6 "2019-06-29T18:03:54Z")

</div>

If the operation (retractall + assert) takes place in code in a PEngines instance that was loaded via the `src_text` parameter, I assume that the multi-thread issue is _not_ a problem, since the code would be local to only one particular PEngines instance, correct?

---

<div class="post-metadata">

**Author:** ![jan](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/jan/32/4_2.png) [@jan](https://swi-prolog.discourse.group/u/jan)\
**Post date:** [June 30, 2019, 7:25am UTC](https://swi-prolog.discourse.group/t/reasonable-way-to-combine-a-retractall-and-assert-operation/882/7 "2019-06-30T07:25:27Z")

</div>

> [@prodog](#):
>
> correct

Yes. The slowdown still happens as Prolog doesn’t know this code can only be accessed by a single thread. Clause GC makes a sweep through the environment stacks of all threads to figure out which predicates are active under which _generation_.
