# Declarative predicates by limiting resources

**URL:** <https://swi-prolog.discourse.group/t/declarative-predicates-by-limiting-resources/1114>\
**Category:** Help!\
**Created:** [August 26, 2019, 8:39pm UTC](https://swi-prolog.discourse.group/t/declarative-predicates-by-limiting-resources/1114 "2019-08-26T20:39:47Z")\
**Posts on this page:** 9\
**Page:** 2

<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:** [August 29, 2019, 8:35pm UTC](https://swi-prolog.discourse.group/t/declarative-predicates-by-limiting-resources/1114/21 "2019-08-29T20:35:19Z")

</div>

> [@swi](#):
>
> Is there a possibility to provide `wrap_predicate` with an option to not wrap the recursive calls?

As yet, no. There is nothing special to recursive calls, which makes this a little hard. And then we have mutual recursive calls, etc. I doubt this is a fruitful direction ☹

term\_expansion can do all this stuf, but the expansion gets rather nasty as well as you have to analyse the bodies for recursive calls. I think the meta-predicate approach is more practical …

---

<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:** [August 29, 2019, 8:53pm UTC](https://swi-prolog.discourse.group/t/declarative-predicates-by-limiting-resources/1114/22 "2019-08-29T20:53:09Z")

</div>

I think I found a solution, it’s a little hacky (😅) but the proof of concept works:

```prolog
wrap_test(Pred/Arity,Depth) :-
   atom(Pred), integer(Arity), Arity>=0,
   functor(Head,Pred,Arity),
   set_flag(outside_recursion,0), % should be named inside_recursion
   wrap_predicate(Head, ideepen, OrigPred,
      (
	 format('inside wrapper~n'),
	 ( get_flag(outside_recursion,IR),
	    IR == 0
	 -> set_flag(outside_recursion,1),
	    format('limit call',[]), call_with_depth_limit(OrigPred, Depth, RD),
	    ( RD == depth_limit_exceeded
	    -> set_flag(outside_recursion,0)
	    ; true
	    )
	 ; format('regular call',[]), call(OrigPred)
	 )
      )
   ).

leaf(l(_)).

btree(bt(A,B)) :-
   ( leaf(A); btree(A) ),
   ( leaf(B); btree(B) ).

```

Query

```prolog
5 ?- call_with_depth_limit(btree(T),3,_).
T = bt(l(_8988), l(_8992)) ;
T = bt(l(_8988), bt(l(_8998), l(_9002))) ;
T = bt(bt(l(_8994), l(_8998)), l(_9002)) ;
T = bt(bt(l(_8994), l(_8998)), bt(l(_9008), l(_9012))) ;
true.

6 ?- wrap_test(btree/1,7).
true.

7 ?- btree(T).
inside wrapper
limit call
T = bt(l(_8592), l(_8596)) ;
inside wrapper
regular call
T = bt(l(_8592), bt(l(_8616), l(_8620))) ;
inside wrapper
regular call
T = bt(bt(l(_8612), l(_8616)), l(_8620)) ;
inside wrapper
regular call
T = bt(bt(l(_8612), l(_8616)), bt(l(_8640), l(_8644))) ;
true.

8 ?- 

```

So it works fine, but with a strange change:  
_ **The depth of 3 in the top level corresponds to the depth of 7 inside the wrapper** _. I think it is because of the strange way in which `call_with_depth_limit/3` counts the recursions. It took me ages to figure this one out.

Of course in the real implementation I will change the flag name to a generated ID based on the head.

Do you see any problem with this approach?  
(I used flags because they are atomic, and probably faster than anything else).

EDIT: the name of the flag is wrong, it comes from a previous iteration. Should be called `inside_recursion` but it doesn’t matter, because it needs to be generated anyway.

---

<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:** [August 30, 2019, 6:40am UTC](https://swi-prolog.discourse.group/t/declarative-predicates-by-limiting-resources/1114/23 "2019-08-30T06:40:48Z")

</div>

> [@swi](#):
>
> Do you see any problem with this approach?  
> (I used flags because they are atomic, and probably faster than anything else).

Flags are shared between threads. That is not what you want. Another problem with global state are possible exceptions that leave the value. Using setup\_call\_cleanup/3 can often solve that.

You can also use prolog\_current\_frame/1 and prolog\_frame\_attribute/3 to detect the recursion. With a lot of intermediate frames that will be slower, but with not too many it is probably faster and doesn’t suffer from all the issues related with global state.

---

<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:** [August 30, 2019, 6:43am UTC](https://swi-prolog.discourse.group/t/declarative-predicates-by-limiting-resources/1114/24 "2019-08-30T06:43:28Z")

</div>

> [@jan](#):
>
> You can also use prolog\_current\_frame/1 and prolog\_frame\_attribute/3 to detect the recursion.

Thanks! I’ll try that. I really didn’t like the global state.

---

<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:** [August 30, 2019, 7:35am UTC](https://swi-prolog.discourse.group/t/declarative-predicates-by-limiting-resources/1114/25 "2019-08-30T07:35:20Z")

</div>

Much cleaner now! :

```prolog
wrap_test(Pred/Arity,Depth) :-
   atom(Pred), integer(Arity), Arity>=0,
   functor(Head,Pred,Arity),
   wrap_predicate(Head, ideepen, OrigPred,
      (
	 prolog_current_frame(F),
	 prolog_frame_attribute(F,parent,P),
	 copy_term(Head,Head1),
	 ( prolog_frame_attribute(P,parent_goal,Head1)
	 -> writeln('regular call'), call(OrigPred)
	 ; writeln('limit call'), call_with_depth_limit(OrigPred, Depth, _)
	 )
      )
   ).

leaf(l(_)).

btree(bt(A,B)) :-
   ( leaf(A); btree(A) ),
   ( leaf(B); btree(B) ).

```

Query

```prolog
40 ?- wrap_test(btree/1,6).
true.

41 ?- btree(T).
limit call
T = bt(l(_3826), l(_3830)) ;
regular call
T = bt(l(_3826), bt(l(_3854), l(_3858))) ;
regular call
T = bt(bt(l(_3850), l(_3854)), l(_3858)) ;
regular call
T = bt(bt(l(_3850), l(_3854)), bt(l(_3882), l(_3886))) ;
true.

```

Turns out level 6 in the wrapper, now corresponds to level 3 in the top level. Had to use `copy_term/2` to get a fresh `Head`.

I like this solution much better. I don’t really care about recursion levels, but only about using `call_with_depth_limit/3` once on first entry.

What do you think? Any problems with it?

---

<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:** [August 30, 2019, 7:53am UTC](https://swi-prolog.discourse.group/t/declarative-predicates-by-limiting-resources/1114/26 "2019-08-30T07:53:13Z")

</div>

This assumes the current call can _unify_ with a parent. That may not be the case. You should use functor/3 twice to create a most general term. Watch out for the possible module qualification!

Otherwise, yes this is what I proposed.

---

<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:** [August 30, 2019, 6:44pm UTC](https://swi-prolog.discourse.group/t/declarative-predicates-by-limiting-resources/1114/27 "2019-08-30T18:44:33Z")

</div>

Good, it is working great. Should I use something like:

```prolog
 setup_call_cleanup(
        '$set_source_module'(OldModule, M),
        % [...code here...]
        '$set_source_module'(OldModule)
 )
``

to take care of the module?
```

---

<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:** [August 31, 2019, 12:50pm UTC](https://swi-prolog.discourse.group/t/declarative-predicates-by-limiting-resources/1114/28 "2019-08-31T12:50:08Z")

</div>

No. That is for some low-level hacks with source manipulation.

---

<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:** [August 31, 2019, 7:14pm UTC](https://swi-prolog.discourse.group/t/declarative-predicates-by-limiting-resources/1114/29 "2019-08-31T19:14:11Z")

</div>

OK, I’ll use `meta_predicate/...`

[Previous page](https://swi-prolog.discourse.group/t/declarative-predicates-by-limiting-resources/1114.md?page=1)
