I have some code that uses when/2 to allow “bi-directional” predicates. When the computation is finished, there shouldn’t be an “frozen” goals, so I do something like this (the actual code is more complex, but I think this captures the gist of it):
But alternatively, I could wrap the call with call_residue_vars/2.
All potential “frozen” variables should be reachable from the top-level X and Y, so I think the effect of both term_attvars/2 and call_residue_vars/2 would be the same but term_attvars/2 should be faster. Is this a correct understanding?
I guess both ways are mostly for debugging. Both are potentially expensive. call_residue_vars/2 is particularly expensive as it watches for constrained variables that are not connected to any particular variable in some specific term. This provides a sound guarantee nothing suspicious is going on. The price is to disable garbage collection for attributed variables.
All in all, I have my doubt using when/2 to avoid the need for explicit checks on modes and acting accordingly is a great idea … It looks elegant. It isn’t generally performing too well and if you need as post-check for attvars this gets even worse while attvars not introduced by the code being checked may be completely sound.
It does make my code simpler, especially in situations where I have multiple predicates that need to be re-ordered – using var/1 instead of when/2 results in a combinatoric explosion of alternatives.
I wonder if the cost of when/2 shows up when running profile/1? (Also, can I assume that the cost of when(nonvar(X), p(X)) is low if X is instantiated?)
(BTW, in this particular situation, I could probably replace the term_attvars(X,[]) by ground(X).)
I was wondering whether profile/1 would show the effects of when/2 overhead or if it’d be lumped together with other aspects of the Prolog engine. (And I first need to make some non-micro test cases, both to provide enough granularity and to see if performance is even an issue.)
It appears that using when/2 adds ~7% overhead to my code (using profile/1), possibly less. There are larger inefficiencies in the code than that, so for now, I’ll stick with the simpler code that when/2 allows.