scryer-prolog
scryer-prolog copied to clipboard
'$call'/N does not call directly
From https://github.com/mthom/scryer-prolog/discussions/1513#discussioncomment-2978271, I take it that '$call'/N
calls the goal directly, without any expansion.
Currently, if I define:
p(X) :- '$call'(g, X).
then this gets compiled to the WAM instructions:
?- wam_instructions(p/1, Is), maplist(portray_clause, Is). put_constant(level(shallow),g,x(2)). execute_n(2).
However, it seems the definition ought to be equivalent to:
p(X) :- g(X).
and that compiles to:
execute(g,1).
On second thought, it seems that this would not make much sense, since code that emits '$call'(p, ...)
where p
is known, may just as well emit p(...)
instead.
I filed this because using '$call'
was suggested in https://github.com/mthom/scryer-prolog/discussions/1513#discussioncomment-2978271.
Everything you've observed is true. The idea for meta-predicates like maplist/N
is to keep their call/N
calls compiled as they are but to wrap their goal arguments in the '$call' functor so that
maplist(portray_clause, Is)
is goal expanded to
maplist('$call'(portray_clause), Is)
Note that call/2
is defined as:
:- non_counted_backtracking call/2.
call(A,B) :-
( var(A) ->
instantiation_error(call/2)
; A = '$call'(C) ->
'$prepare_call_clause'(D,E,C,B),
'$call_with_inference_counting'('$call'(E:D))
; '$prepare_call_clause'(D,E,A,B),
expand_goal(call(E:D),E,call(F)),
'$call_with_inference_counting'('$call'(F))
).
expand_goal/3
could still be applied to the goal arguments prior to wrapping, but I'm not totally certain on how it should apply to incomplete goals. The more aggressive inlining you proposed in the discussion doesn't pose this problem.
We can bridge the two ideas as follows. We generate an auxiliary predicate, as you suggested. The goal arguments are expanded via expand_goal/3
before the auxiliary predicate is entered where the expanded goals are invoked via $call
in the call sites of the original predicate.
Is there hope that also #1390 could be addressed here? The idea would be to have two cases: with expansion (and thus losing information about local variables) and as the frequent fallback without expansion and there retaining that info.
where the expanded goals are invoked via $call in the call sites of the original predicate.
I have filed #1516 to clarify what I mean with the auxiliary predicate. I think '$call'
is not necessary in this case, because the auxiliary predicate can be invoked directly, and so no meta-call seems to be needed.
You're right to point out that '$call/N' isn't necessary. Two points in favor of the approach I outlined above, though:
- Compiling a
maplist_for_p
for every predicatep
used in maplist would generate a lot of additional code, a problem at least until code GC is implemented, and, - It would remove much of the goal expansion overhead for all predicates using
call/N
, not just those oflibrary(lists)
et al.
Then again, library(lists)
will need an ad hoc approach either way, since it's used in the bootstrapping process and so its compilation isn't subject to expand_goal
.
and so no meta-call seems to be needed.
What about calling an integer? This case needs to be recognized here somehow.
I think this can be safely closed once rebis-dev
is merged (see #1516), since the relevant goals are now automatically inlined.
Thank you a lot!