# Det-if?

**URL:** <https://swi-prolog.discourse.group/t/det-if/7175>\
**Category:** General\
**Tags:** determinism\
**Created:** [January 19, 2024, 3:17pm UTC](https://swi-prolog.discourse.group/t/det-if/7175 "2024-01-19T15:17:24Z")\
**Posts on this page:** 12\
**Page:** 1

<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:** [January 19, 2024, 3:17pm UTC](https://swi-prolog.discourse.group/t/det-if/7175/1 "2024-01-19T15:17:24Z")

</div>

Some Prolog predicates are deterministic under certain (mode) conditions. One way to program this is e.g.

```prolog
p(X) :-
     ( ground(X)
     -> q(X), !
     ; q(X)
     ).

```

This is a bit cumbersome to write though. I think I’ve seen a meta predicate to generalize this. Does anyone know about such practice? Otherwise, I’d consider this. Next, we also need a good library …

```prolog
det_if(Cond, Goal) :-
    ( Cond
    -> Goal,
        !
    ; Goal
    ).

```

---

<div class="post-metadata">

**Author:** ![kwon-young](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/kwon-young/32/4841_2.png) [@kwon-young](https://swi-prolog.discourse.group/u/kwon-young)\
**Post date:** [January 19, 2024, 3:56pm UTC](https://swi-prolog.discourse.group/t/det-if/7175/2 "2024-01-19T15:56:05Z")

</div>

Shouldn’t this be the responsability of the predicate `q` to be deterministic under these conditions ?

Do you have a specific example in mind ?

---

<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:** [January 19, 2024, 4:16pm UTC](https://swi-prolog.discourse.group/t/det-if/7175/3 "2024-01-19T16:16:30Z")

</div>

> [@kwon-young](#):
>
> Shouldn’t this be the responsability of the predicate `q` to be deterministic under these conditions ?

Surely, you want to put the responsibility of being (semi)det as low as possible. In this case (and I’ve seen quite a few similar ones), I have a notdet predicate that produces properties. If we want to check that some property exists, we’d like to write

```
  xyz_property(p(1)).

```

But, the underlying predicate makes this non-det. Now, I can re-implement it as below. This is nice and compact and expresses exactly what I want.

```prolog
xyz_property(P) :-
    det_if(ground(P), xyz_property_(P)).

xyz_property_(P) :-
   ...

```

Thinking about it, distinct/1,2 can also be improved this way. Tabling already does this: if the goal is ground and it finds an answer it considers the table immediately complete.

---

<div class="post-metadata">

**Author:** ![brebs](https://avatars.discourse-cdn.com/v4/letter/b/e9c0ed/32.png) [@brebs](https://swi-prolog.discourse.group/u/brebs)\
**Post date:** [January 20, 2024, 7:59am UTC](https://swi-prolog.discourse.group/t/det-if/7175/4 "2024-01-20T07:59:28Z")

</div>

> [@kwon-young](#):
>
> `q` to be deterministic

All the predicates that `q` calls would also have to be deterministic.

How to improve determinism, whilst decreasing code elegance the least? 😀

append/3 is an example:

```prolog
?- append([a,b], ObviouslyEmpty, [a,b]).
ObviouslyEmpty = []. % Deterministic

?- append(ObviouslyEmpty, [a,b], [a,b]).
ObviouslyEmpty = [] ;
false. % Unwanted choicepoint

```

Removing that unwanted choicepoint in some way, by adding relatively ugly code, is easy.

Removing that unwanted choicepoint in an _elegant_ way, is not easy.

I suppose the aim here is to prevent having to add code which is not increasing the fundamental correctness of the code.

It would be really nice if the Prolog engine could infer determinism by itself - do we have AI yet? 😃

---

<div class="post-metadata">

**Author:** ![peter.ludemann](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/peter.ludemann/32/48_2.png) [@peter.ludemann](https://swi-prolog.discourse.group/u/peter.ludemann)\
**Post date:** [January 20, 2024, 11:13pm UTC](https://swi-prolog.discourse.group/t/det-if/7175/5 "2024-01-20T23:13:26Z")

</div>

> [@jan](#):
>
> Some Prolog predicates are deterministic under certain (mode) conditions.

When writing foreign predicates, I’ve fairly often had the situation where I check the 1st arg for being a variable, such as the way current\_prolog\_flag/2 works. This seems to be a common use case, with two alternative goals rather than the same goal with a cut:

```prolog
p(X, Y) :-
    ( var(X)
    -> lookup_something(X, Y) % call deterministic goal
    ; backtracking_lookup(X, Y)
    ).

```

and there can also be a variant:

```prolog
p(X, Y) :-
    ( var(X)
    -> p_lookup(X, Y) % call deterministic goal
    ; var(Y)
    -> p_lookup_reverse(Y, X) % call deterministic goal
    ; p_lookup_backtracking(X, Y)
    ).

```

In some cases, I can tell if there are no more choices and remove a choicepoint. E.g., the code for between/3 proactively computes the next result and checks if it’s the last one, in which case it does a “return” rather than “return with choiceoint” (`PL_retry_address()`).

---

<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:** [January 21, 2024, 8:54am UTC](https://swi-prolog.discourse.group/t/det-if/7175/6 "2024-01-21T08:54:44Z")

</div>

That us more or less what needs to happen, but it is a little ugly 🙂 The det\_if/2 would deal with the (I think) fairly common case that the implementation for both cases is the same, but we cannot guarantee determinism for the ground case.

---

<div class="post-metadata">

**Author:** ![peter.ludemann](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/peter.ludemann/32/48_2.png) [@peter.ludemann](https://swi-prolog.discourse.group/u/peter.ludemann)\
**Post date:** [January 21, 2024, 6:04pm UTC](https://swi-prolog.discourse.group/t/det-if/7175/7 "2024-01-21T18:04:42Z")

</div>

> [@jan](#):
>
> cannot guarantee determinism for the ground case

I don’t recall encountering this … do you have an example?  
I have had situations where the predicate is deterministic or not, depending on the next value, for example between/3.

---

<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:** [January 22, 2024, 3:01pm UTC](https://swi-prolog.discourse.group/t/det-if/7175/8 "2024-01-22T15:01:42Z")

</div>

> [@peter.ludemann](#):
>
> I don’t recall encountering this … do you have an example?

I have seen them before. In this case I have a file holding Prolog (feature) terms and a nondet predicates that backtracks through these terms. A predicate `has_feature(File, Term)` should be semidet if Term is ground and nondet otherwise. Of course, the caller can use once/1, but that is rather inelegant. So, the implementation becomes the rather ugly

```prolog
has_feature(File, Feature) :-
    ground(Feature),
    !,
    once(term_in_file(File, Feature)).
has_feature(File, Feature) :-
    term_in_file(File, Feature).

```

---

<div class="post-metadata">

**Author:** ![peter.ludemann](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/peter.ludemann/32/48_2.png) [@peter.ludemann](https://swi-prolog.discourse.group/u/peter.ludemann)\
**Post date:** [January 22, 2024, 6:44pm UTC](https://swi-prolog.discourse.group/t/det-if/7175/9 "2024-01-22T18:44:43Z")

</div>

`term_in_file(File, Feature)` seems similar to member/2 (I presume it can backtrack through multiple occurrences of matching `Feature` in `File`), so perhaps what you want is the analogue of memberchk/2: `term_in_file_chk(File, Feature)`?

---

<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:** [January 23, 2024, 8:28am UTC](https://swi-prolog.discourse.group/t/det-if/7175/10 "2024-01-23T08:28:28Z")

</div>

> [@peter.ludemann](#):
>
> `term_in_file_chk`

That is a solution, but IMO less elegant. You need an extra predicate and the interface becomes less logical than it could be. That would, for 99% of the applications, also hold for member/2, but here the list also needs to be ground. If the list holds non-ground terms, backtracking using a ground member can be just fine. The well known Einstein riddle solutions often use that.

---

<div class="post-metadata">

**Author:** ![peter.ludemann](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/peter.ludemann/32/48_2.png) [@peter.ludemann](https://swi-prolog.discourse.group/u/peter.ludemann)\
**Post date:** [January 23, 2024, 6:36pm UTC](https://swi-prolog.discourse.group/t/det-if/7175/11 "2024-01-23T18:36:20Z")

</div>

> [@jan](#):
>
> That is a solution, but IMO less elegant.

Abusing `=>` notation a bit:

```prolog
:- meta_predicate det_cond_goal(0, 0).

det_cond_goal(Cond, Goal), Cond, Goal => true.
det_cond_goal(_Cond, Goal) => Goal.

```

```prolog
has_feature(File, Feature) :-
    det_cond_goal(ground(Feature), 
                  term_in_file(File, Feature)).

```

---

<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:** [January 24, 2024, 8:14am UTC](https://swi-prolog.discourse.group/t/det-if/7175/12 "2024-01-24T08:14:11Z")

</div>

det\_cond\_goal/2 is just another name and implementation for det\_if/2, no? The original question is whether there is current practice for such a predicate and whether or not we want it in a library? And, if we decide we want it while there is no current practice, how we name it?

P.s., the implementation is wrong. You have to commit twice, both after the condition _and_ after the goal.
