Page 1 of 1

Hook predicates and static binding - incompatible?

Posted: Mon Oct 20, 2008 4:31 pm
by Parker
I'd like to use static binding for category predicates called from an object - ie switch from :: to :. However, I use a hook object to allow compatibility across different prolog systems and have run into a problem.

Here is the loader. An object test should use predicates from category utils. But utils has predicates affected by hook.

Code: Select all

:- initialization(
        (logtalk_load([hook]),
         logtalk_load([utils],[reload(skip),hook(hook)]),
	 logtalk_load([test],[events(on),xmldocs(off)])
        )). 
All the code compiles fine if the call to the category predicate is with ::. But when the call is with : object test is giving me an existence error:

Code: Select all

    compiling object test... 
        ERROR!    existence_error(procedure, yap_flag(counter, _G9647, 0))
                  in clause: test:- :yap_flag(counter, _G9647, 0)
                  above line: 7
Is this because I'm not using these features correctly or could it be they're not compatible?

Cheers,
Parker

Re: Hook predicates and static binding - incompatible?

Posted: Mon Oct 20, 2008 7:18 pm
by Paulo Moura
Hi Parker!

Nice to ear from you again.
Parker wrote: I'd like to use static binding for category predicates called from an object - ie switch from :: to :. However, I use a hook object to allow compatibility across different prolog systems and have run into a problem.
Note that, while the :/1 control construct allows the use of static binding when calling imported category predicates, the semantics of the control constructs ::/2 and :/1 are different. The first uses predicate (declaration and definition) lookups starting in "self" while the later uses predicate lookups starting in "this" and restricted to the imported categories. Switching from ::/2 to :/1 is not a problem as long as the predicate is not being redefined in a descendant object.
Parker wrote: Here is the loader. An object test should use predicates from category utils. But utils has predicates affected by hook.

Code: Select all

:- initialization(
        (logtalk_load([hook]),
         logtalk_load([utils],[reload(skip),hook(hook)]),
	 logtalk_load([test],[events(on),xmldocs(off)])
        )). 
All the code compiles fine if the call to the category predicate is with ::. But when the call is with : object test is giving me an existence error:

Code: Select all

    compiling object test... 
        ERROR!    existence_error(procedure, yap_flag(counter, _G9647, 0))
                  in clause: test:- :yap_flag(counter, _G9647, 0)
                  above line: 7
Is this because I'm not using these features correctly or could it be they're not compatible?
I guess you're defining clauses for goal_expansion/2 in your hook object. You need to add a clause for :/1 goals. The same for ::/1 goals. For example:

Code: Select all

goal_expansion(::Goal, ::ExpandedGoal) :-
    nonvar(Goal),    % sanity check for prevening endless loops
    goal_expansion(Goal, ExpandedGoal).

goal_expansion(:Goal, :ExpandedGoal) :-
    nonvar(Goal),    % sanity check for prevening endless loops
    goal_expansion(Goal, ExpandedGoal).

goal_expansion(..., ...).
I'm surprised that your code works for ::/1 calls but it's hard to explain without seeing your hook code.

Hope this helps. Let me know if the solution above solves your problem. Cheers,

Paulo

Re: Hook predicates and static binding - incompatible?

Posted: Fri Oct 24, 2008 2:18 pm
by Parker
Paulo Moura wrote: Nice to ear from you again.
Likewise!
Is this because I'm not using these features correctly or could it be they're not compatible?
Ooops. It's my fault. I missed out the category import so the existence error was perfectly correct. With that corrected, it appears to work fine.
Paulo Moura wrote: I guess you're defining clauses for goal_expansion/2 in your hook object. You need to add a clause for :/1 goals. The same for ::/1 goals. For example:

Code: Select all

goal_expansion(::Goal, ::ExpandedGoal) :-
    nonvar(Goal),    % sanity check for prevening endless loops
    goal_expansion(Goal, ExpandedGoal).

goal_expansion(:Goal, :ExpandedGoal) :-
    nonvar(Goal),    % sanity check for prevening endless loops
    goal_expansion(Goal, ExpandedGoal).

goal_expansion(..., ...).
I'm surprised that your code works for ::/1 calls but it's hard to explain without seeing your hook code.
The hook code doesn't specifically address ::/1 calls. BTW, the code should look familiar to you :wink:

Code: Select all

    hook((Head :- Body), [(Head :- TBody)]) :-
        conjunction(Body, TBody).

    conjunction((Goal1; Goal2), (TGoal1; TGoal2)) :-
        !,
        conjunction(Goal1, TGoal1),
        conjunction(Goal2, TGoal2).
    conjunction((Goal1 -> Goal2), (TGoal1 -> TGoal2)) :-
        !,
        conjunction(Goal1, TGoal1),
        conjunction(Goal2, TGoal2).
    conjunction(\+ (Goal, Goals), \+ (TGoal, TGoals)) :-
        !,
        conjunction(Goal, TGoal),
        conjunction(Goals, TGoals).
    conjunction((Goal, Goals), (TGoal, TGoals)) :-
        !,
        conjunction(Goal, TGoal),
        conjunction(Goals, TGoals).
    conjunction(\+ Goal, \+ TGoal) :-
        !,
        transform(Goal, TGoal).

    conjunction(Goal, TGoal) :-
        transform(Goal, TGoal).

    % replace bb_put/2 with true
    transform(bb_put(_,_),true) :- !.

    % replace bb_get/2 with true
    transform(bb_get(_,_),true) :- !.

    % replace number_atom/2 with true
    transform(number_atom(_,_),true) :- !.

    transform(Goal, Goal).

    goal_expansion(bb_put(_,_),true).
    goal_expansion(bb_get(_,_),true).
    goal_expansion(number_atom(_,_),true).
Paulo Moura wrote: Hope this helps. Let me know if the solution above solves your problem. Cheers,
Paulo
Thanks Paulo. Curiously, preliminary testing shows no noticeable speed improvement for my code. Perhaps there are no :/1 calls on the critical path (although I doubt this). Is it possible static binding is not being used for some reason? To test this I compared compiled code when switching from ::/1 to :/1 and the calls to $lgt_send_to_self_nv are removed, which seems to be correct. I suppose I was over-optimistic about the improvement. However my results are consistent with your benchmarks (c2 vs c1) here http://logtalk.org/performance.html

Running Logtalk: 2.32.2 on SWI 5.6.57

Cheers,
Parker,

FYI: reply notification is not working on this forum even when subscribed to a topic - just in case you didn't know.

Re: Hook predicates and static binding - incompatible?

Posted: Fri Oct 24, 2008 3:21 pm
by Paulo Moura
Parker wrote: The hook code doesn't specifically address ::/1 calls. BTW, the code should look familiar to you :wink:

Code: Select all

    hook((Head :- Body), [(Head :- TBody)]) :-
        conjunction(Body, TBody).

    conjunction((Goal1; Goal2), (TGoal1; TGoal2)) :-
        !,
        conjunction(Goal1, TGoal1),
        conjunction(Goal2, TGoal2).
    conjunction((Goal1 -> Goal2), (TGoal1 -> TGoal2)) :-
        !,
        conjunction(Goal1, TGoal1),
        conjunction(Goal2, TGoal2).
    conjunction(\+ (Goal, Goals), \+ (TGoal, TGoals)) :-
        !,
        conjunction(Goal, TGoal),
        conjunction(Goals, TGoals).
    conjunction((Goal, Goals), (TGoal, TGoals)) :-
        !,
        conjunction(Goal, TGoal),
        conjunction(Goals, TGoals).
    conjunction(\+ Goal, \+ TGoal) :-
        !,
        transform(Goal, TGoal).

    conjunction(Goal, TGoal) :-
        transform(Goal, TGoal).

    % replace bb_put/2 with true
    transform(bb_put(_,_),true) :- !.

    % replace bb_get/2 with true
    transform(bb_get(_,_),true) :- !.

    % replace number_atom/2 with true
    transform(number_atom(_,_),true) :- !.

    transform(Goal, Goal).

    goal_expansion(bb_put(_,_),true).
    goal_expansion(bb_get(_,_),true).
    goal_expansion(number_atom(_,_),true).
Why do you need the conjunction/2 predicate? It should be enough to simply define clauses for goal_expansion/2 in your hook object. Logtalk automatically calls goal_expansion/2 for every goal in the body of object (and category) predicate clauses. Am I missing something?
Parker wrote: Thanks Paulo. Curiously, preliminary testing shows no noticeable speed improvement for my code. Perhaps there are no :/1 calls on the critical path (although I doubt this). Is it possible static binding is not being used for some reason? To test this I compared compiled code when switching from ::/1 to :/1 and the calls to $lgt_send_to_self_nv are removed, which seems to be correct. I suppose I was over-optimistic about the improvement. However my results are consistent with your benchmarks (c2 vs c1) here http://logtalk.org/performance.html

Running Logtalk: 2.32.2 on SWI 5.6.57
Logtalk 2.32.2 is 3 months old. The latest stable release (2.33.2) gives you some nice improvements and new features including a new debugger feature that you requested long ago.

I assume that you're compiling the categories in a separate logtalk_load/2 call using the reload(skip) option before the calls that load the objects that import the category. The actual improvement depends on the amount of work your category predicates are performing. If this work is significant (performance-wise), the overhead of dynamic binding (when using ::/2 to call the category predicates) becomes negligible.

To check that static binding is doing its magic, check the generated Prolog files for your objects. The category predicates should be being called directly.

Btw, SWI-Prolog 5.7.x seems to be getting stable enough for daily use. It should provide a nice speed boost for your application.
Parker wrote: FYI: reply notification is not working on this forum even when subscribed to a topic - just in case you didn't know.
Yes, email notifications seem to not be working. Again. Meantime, the most reliable way to get notified is to subscribe to this forums RSS feed. For Logtalk development news and ticket updates subscribe to the RSS feeds on http://trac.logtalk.org/.

Cheers,

Paulo

Re: Hook predicates and static binding - incompatible?

Posted: Sun Nov 09, 2008 12:03 am
by Paulo Moura
Paulo Moura wrote: Yes, email notifications seem to not be working. Again. Meantime, the most reliable way to get notified is to subscribe to this forums RSS feed. For Logtalk development news and ticket updates subscribe to the RSS feeds on http://trac.logtalk.org/.
The problem with email notifications should hopefully be solved now. Nevertheless, subscribing to the RSS feeds is still recommended, both for keeping tabs in the forums and on Logtalk development.

Cheers,

Paulo