Lambda compiler?

While stepping through the debugger to examine a failure in a lambda, it occurred to me that they invoke a decent amount of machinery, and also that much of this might be compiled away. Isn’t it the case that:

f():-
   maplist([X,Y]>>g(X,Y), [1,2,3], [4,5,6], R).

can could be automatically transformed to:

f():-
   maplist(f_lambda_0, [1,2,3], [4,5,6]).

f_lambda_0(X,Y):- g(X,Y).

Apart from being faster, I think this is considerably easier to debug – avoiding the need of stepping through the lambda library onion, making parameter count mismatches clear, etc…

For closures:

f(C):-
   maplist({C}\[X,Y]>>g(X,Y), [1,2,3], [4,5,6]).

would need to be transformed to something like:

f(C):-
   f_lambda_c_0(C, F),     % Obtain a FC(X,Y) predicate internally bound to C. Something, something, <insert magic here>
   maplist(F, [1,2,3], [4,5,6]).

Is it possible to precompile lambdas in this way, automatically adding the implementation predicate? It would benefit both efficiency and debug-clarity?

In the case

f :- maplist([X,Y]>>g(X,Y), [1,2,3], [4,5,6]).   % R removed, no need for f() 

this is actually what is being done. You were looking at the interpreter I think.

with

?- [user].
:- use_module(library(apply)).
:- use_module(library(yall)).
f :- maplist([X,Y]>>g(X,Y), [1,2,3], [4,5,6]).

check it out:

?- listing(f).
f :-
    maplist('__aux_yall_21ae7b3e40ba3b15f814d56a704f118bb9ba04f8',
            [1, 2, 3],
            [4, 5, 6]).

?- listing('__aux_yall_21ae7b3e40ba3b15f814d56a704f118bb9ba04f8').
'__aux_yall_21ae7b3e40ba3b15f814d56a704f118bb9ba04f8'(A, B) :-
    g(A, B).

For the second part, {C}/[X,Y]>>g(X,Y) (forward slash, not backward slash) is a notation to indicate the free variables in the lambda expression. It is not a closure (in the Prolog sense, not the functional programming sense), which is a term that is a partially-filled call to a procedure - for example:

call(atom_concat,prefix,suffix,R).   % No argument prefilled (is it still a closure? probably not)
call(atom_concat(prefix),suffix,R).  % One leftmost argument prefilled - a Prolog closure
call(atom_concat(prefix,suffix),R).  % Two leftmost arguments prefilled - a Prolog closure
call(atom_concat(prefix,suffix,R)).  % All arguments filled (is it still a closure? one could say that yes) 

So let’s see, with

[user]
f(C) :- maplist({C}/[X,Y]>>g(X,Y), [1,2,3], [4,5,6]).

then

?- listing(f/1).
f(A) :-
    maplist('__aux_yall_f983382b55d1a34c56dc30d92ba52b0c5da16719'(A),
            [1, 2, 3],
            [4, 5, 6]).

?- listing('__aux_yall_f983382b55d1a34c56dc30d92ba52b0c5da16719').
'__aux_yall_f983382b55d1a34c56dc30d92ba52b0c5da16719'(_, A, B) :-
    g(A, B).

Sounds good already. What the run-time or WAM does with it I don’t know.

Note the the >> notation is just another term which is rewritten to “basic Prolog” first using term rewriting of the source (see expend_term/2)

1 Like