-
Notifications
You must be signed in to change notification settings - Fork 139
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Rethinking goal expansion #2532
Comments
One remark: The removal of unnecessary |
Now that I have a better understanding per #2598 (comment), it seems to me that these predicates fill a very similar function to the macro-expansion phase in lisp languages. I would suggest one possibility that would be more efficient, if you only want a subset of goals/terms in a module rather than ALL terms, would This is similar to designating something as a macro in a lisp. Also, using a term expansion or goal expansion directive near the predicate it operates on would make code a lot more readable, because it can be very unintuitive by looking at a predicate in isolation, with no other information, metadata, etc, that it is meant to be used in conjunction with an expansion mechanism. |
You can already have it for your own goal expansions: expansion_enabled(bar(_)).
user:goal_expansion(G, X) :-
nonvar(G),
expansion_enabled(G),
... .
foo(X) :-
bar(X). % bar/1 will be expanded |
Note that the main issue is not whether to perform goal expansion at all, or which terms should be subject to goal or term expansion. As I see it, the main issue we are facing is: How do we express that in certain situations, we want to refer to the actual source code, while in others we want to refer to the expanded version? For instance, when showing a listing or program slice, we would like to be able to show the actual source code, and maybe selectively also the expanded version. |
Hmmm. In lisp this problem is addressed by I could imagine a predicate that returns as leaf answers progressively expanded by goals/terms until the base case expansion. I don't know enough yet about how term expansion or goal expansion work yet to imagine the internals, however for this and other reasons it makes me wonder if some kind of goal/term expansion with continuation might be desirable. |
In the case 3, dynamic goals such as
At compile-time, the Allowing goal expansion in dynamic goals requires run-time goal expansion. Now goal expansion can be blocked with If the reference implementation of |
Not sure what you mean. But yes, the reference implementation of |
Before we continue, a small detour. Does Can a goal expand into a term that is not a body for execution? (i.e. |
Goal expansion is something the standard does not define. For good reasons as we see even now, thirty years later, there is no clear convergence. The standard defines, however, something very much related. This term-to-body conversion. There the following happens: variables-as-goals are wrapped with Given all that, it seems most sensible to perform such an expansion prior to the final term-to-body conversion. And at least that is what is done for DCGs in DTR 13211-3. Also, think of it, the purpose of that conversion is to ensure that we do not have numbers or variables as goals, so it seems to me we have to put this last.
Given above considerations, yes, it can, but then immediately the final conversion will catch it and produce an error. |
It may make things simpler and goal expansion more troublesome. The predicate |
Good point. This shows that goal expansion is much more restricted than general term expansion. As for DCGs, the entire body is converted in one fell swoop using the logical expansion and (effectively) replacing variables in place of a non-terminal by |
Without module, an implementation of Example
:- module(reif0, [if_/3]).
:- use_module(library(dif), [dif/2]).
:- use_module(library(lists), [append/3]).
is_convertible_term(T) :-
catch((! ; T), error(type_error(callable,_),_), false).
is_body(T) :-
\+ \+ (
term_variables(T, Vs),
append(Vs, _, [0|Vs]),
is_convertible_term(T)
).
has_cut(T) :-
var(T), !,
throw(error(instantiation_error,has_cut/1)).
has_cut(!) :-
!.
has_cut((G0,G)) :-
!,
has_cut(G0),
has_cut(G).
has_cut((G0;G)) :-
!,
has_cut(G0),
has_cut(G).
has_cut((_->G)) :-
!,
has_cut(G).
has_cut(_).
user:goal_expansion(if_(If_1, Then_0, Else_0), G_0) :-
ugoal_expansion(if_(If_1, Then_0, Else_0), G_0).
% Bug?
% :- use_module(library(format)).
% :- use_module(library(debug)).
% user:goal_expansion(G0, G) :-
% portray_clause(user_error, "almost"),
% ugoal_expansion(G0, G).
body_expansion(G_0, G) :-
is_body(G_0), \+ has_cut(G_0), !,
G = G_0.
body_expansion(G_0, call(G_0)).
=(X, Y, T) :-
X == Y, !,
T = true.
=(X, Y, T) :-
X \= Y, !,
T = false.
=(X, X, true).
=(X, Y, false) :-
dif(X, Y).
% % Specialized, inlining of (=)/3
ugoal_expansion(if_(Cond_1, Then0_0, Else0_0), G) :-
subsumes_term((_=_), Cond_1), !,
(X=Y) = Cond_1,
body_expansion(Then0_0, Then_0),
body_expansion(Else0_0, Else_0),
G = ( X == Y -> Then_0
; X \= Y -> Else_0
; X = Y, Then_0
; dif(X,Y), Else_0
).
% Many other specialized.
% General
ugoal_expansion(if_(Cond_1, Then0_0, Else0_0), G) :-
body_expansion(Then0_0, Then_0),
body_expansion(Else0_0, Else_0),
G = ( call(Cond_1, T),
( T == true
-> Then_0
; T == false
-> Else_0
; must_be(boolean, T)
)
).
% Reference.
if_(Cond_1, Then_0, Else_0) :-
( call(Cond_1, T),
( T == true
-> call(Then_0)
; T == false
-> call(Else_0)
; must_be(boolean, T)
)
).
This implementation doesn't block goal expansion for goal which is a valid body for execution and doesn't have cut. It seems that only one implementation can be reference if dynamic goals don't expand. |
No need to check |
Currently with Scryer:
Since Some goals have to be made invisible (no In general, the same goes for term expansion:
Before |
That's interesting. How is it normally done? What alternatives are there? |
No idea. I'm seeing the flaws of the current implementation (by doing small tests).
This is what this issue is discussing about. I don't know other alternatives (actually SICStus is one but I'm not testing it, there is a documentation though) but I'm proposing that the alternative should fix the raised issues. In the case of goal expansion, the alternative I'm proposing is one that doesn't expand dynamic goal, only works with valid bodies for execution, doesn't expand at run-time. But if goal expansion is done at run-time then I have to rethink another proposal. In the case of term expansion, the alternative I'm proposing is one that doesn't silence errors. |
Goal expansion would really benefit from thinking more about it, and maybe changing the overall approach. See also #2433 (comment).
At least the following concerns come to mind:
call(!)
is different from a plain!
appearing in a clause, and a correct expansion must be careful not to replace a meta-called goal!/0
by an actual!
in the resulting code.call/1
, even though such an "overly cautious" expansion could itself be mitigated by other means that are maybe also worth implementing by themselves, or even also by goal expansion rules.maplist(Goal, ...)
, or even justGoal
(i.e.,call(Goal)
). Should such cases even be goal-expanded? It may seem pointless overhead to produce an expansion at run-time instead of executing the goal directly. On the other hand, this may lead to massive performance differences for instance when timing goals on the toplevel vs. in programs, and that may give casual Prolog programmers a completely wrong impression about the "actual" performance of constructs that occur in programs. Still worth thinking about, and maybe just the toplevel should do such a "dynamic" expansion?library(diadem)
(see Porting library(diadem) to Scryer Prolog #840) or even in answers ofclause/2
. At the same time, there must be a way to inspect the expansion so that we can see it when needed. Is there a way to convey the intent to a goal expansion mechanism, for instance by stating exceptions to the expansion?meta_predicate/1
declarations?library(lambda)
seems to need a very similar or at least closely related mechanism. See also Goal expansion in lib reif #2433 (comment).The text was updated successfully, but these errors were encountered: