You can implement a functor that does nothing but call whatever is passed to it ,
then in Your goal expansion promote Your expansion to use that functor ,
now Your goal expansion can check if a goal has already been promoted to use Your functor ,
in which case the expansion has already been done .
Example below rewrites every goal to use the “expanded” predicate
and does not rewrite any goal that is already an “expanded” goal .
IMHO it is a mistake to make the behaviour of goal_expansion different at a system level to tackle this problem .
A user may wish to see that goal come back again .
For example perhaps their implementation in goal_expansion uses assert or global variables
such that the second time around a different expansion is provoked .
~~~~~~~~ %&ZHcx;. ~~~~~~~~
?- [user] .
expanded(_anything_) :- _anything_ .
\+ _saw_ =.. [expanded|_] ,
_now_ = expanded(_NOW_) .
?- test .
?- expand_goal(hello,G) .
G = expanded(writeln(hello)).
G = expanded(writeln(goodbye)).
G = whatever.
?- [user] .
_enabled_ = tabling(enabled(_saw_)) ,
_stored_ = tabling(stored(_saw_)) ,
_store_ = assertz(tabling(stored(_saw_))) ,
_1_ = (\+ _enabled_ , _saw_ ) ,
_2_ = (_enabled_ , _stored_ ) ,
_3_ = (_enabled_ , \+ _stored_ , _saw_ , _store_) ,
_now_ = (_1_ ; _2_ ; _3_) .
?- main .
?- tabling(stored(T)) , format("~N~q.~n",[T]) , false ; true .