Skip to content
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

Lambda with a predicate: existence error after "two unifications" #2619

Open
haijinSk opened this issue Oct 12, 2024 · 12 comments
Open

Lambda with a predicate: existence error after "two unifications" #2619

haijinSk opened this issue Oct 12, 2024 · 12 comments

Comments

@haijinSk
Copy link

haijinSk commented Oct 12, 2024

Loading lambda and, for example, lists library:

$ scryer-prolog -f 
?- use_module(library(lambda)).
   true.
?- use_module(library(lists)).
   true.

Through "one unification", so to speak, as expected:

?- Lambda = \N^length("",N), call(Lambda,N).
   Lambda = \0^length([],0), N = 0. 

The unexpected existence error examples:

?- [Lambda] = [\N^length("",N)], call(Lambda,N).
   error(existence_error(procedure,length/2),length/2).

?- Lambda = \N^length("",N), Pred = Lambda, call(Pred,N).
   error(existence_error(procedure,length/2),length/2).

But, for example, this works as expected:

?- [Lambda] = [\N^(N=1)], call(Lambda,N).
   Lambda = \1^(1=1), N = 1.

?- Lambda = \N^(N=1), Pred = Lambda, call(Pred,N).
   Lambda = \1^(1=1), N = 1, Pred = \1^(1=1).

PS: It's not about the lists library, I could consult my own file with my own predicate in a lambda expression used in this way with the same "existence error" effect.

@haijinSk haijinSk changed the title Lambda with a predicate: existence error after unification Lambda with a predicate: existence error after "two unifications" Oct 12, 2024
@bakaq
Copy link
Contributor

bakaq commented Oct 12, 2024

Probably related to #2255.

@haijinSk haijinSk changed the title Lambda with a predicate: existence error after "two unifications" Called bambda with a predicate: existence error after "two unifications" Oct 12, 2024
@haijinSk haijinSk changed the title Called bambda with a predicate: existence error after "two unifications" Lambda with a predicate: existence error after "two unifications" Oct 12, 2024
@UWN
Copy link

UWN commented Oct 13, 2024

In SICStus, the existence error also indicates the module where that error occurs.

@UWN
Copy link

UWN commented Oct 13, 2024

Could you disassemble the WAM code just to see what has been compiled there?

@haijinSk
Copy link
Author

haijinSk commented Oct 13, 2024

:- use_module(library(lambda)).
:- use_module(library(lists)).
:- use_module(library(diag)).

% ?- test1.
%     true.
test1 :-
  Lambda = \N^length("",N),
  call(Lambda,N).

% ?- test2.
%    error(existence_error(procedure,length/2),length/2).
test2 :-
  Lambda = \N^length("",N),
  Pred = Lambda,
  call(Pred,N).

test1: The "as expected" case:

?- wam_instructions(test1/0,Instrutions);false.
   % [...]
   Instrutions = 
[allocate(1),
put_variable(x(1),1),
put_structure(length,2,x(3)),
set_constant([]),
set_variable(y(1)),
put_structure(^,2,x(4)),
set_value(y(1)),
set_value(x(3)),
put_structure(\,1,x(2)),
set_value(x(4)),
call(=,2),
put_structure(length,2,x(3)),
set_constant([]),
set_value(y(1)),
set_constant('$index_ptr'(6800)),
put_structure(:,2,x(4)),
set_constant(user),
set_value(x(3)),
put_structure(^,2,x(3)),
set_value(y(1)),
set_value(x(4)),
set_constant('$index_ptr'(19275)),
put_structure(:,2,x(4)),
set_constant(user),set_value(x(3)),
put_structure(\,1,x(3)),
set_value(x(4)),
set_constant('$index_ptr'(19371)),
put_structure(:,2,x(1)),
set_constant(user),
set_value(x(3)),
put_value(y(1),2),
deallocate,
execute(call,2)]
;  false.

test2: The "existence error" case:

?- wam_instructions(test2/0,Instrutions).
   Instrutions = 
[allocate(3),
put_variable(y(1),1),
put_structure(length,2,x(3)),
set_constant([]),
set_variable(y(2)),
put_structure(^,2,x(4)),
set_value(y(2)),
set_value(x(3)),
put_structure(\,1,x(2)),
set_value(x(4)),
call(=,2),
put_variable(y(3),1),
put_value(y(1),2),
call(=,2),
put_unsafe_value(3,1),
put_value(y(2),2),
deallocate,
execute(call,2)].

@UWN
Copy link

UWN commented Oct 13, 2024

Any easy way to tell what module we are in? (I hoped it would show here, but this set_constant('$index_ptr'(19371)), is obscure.)

@haijinSk
Copy link
Author

haijinSk commented Oct 13, 2024

Any easy way to tell what module we are in?

I'm sorry, everything I know is here. I did not explicitly defined any module, I only loaded the file with the predicates test1/0 and test2/0 from the command line.

@triska
Copy link
Contributor

triska commented Oct 13, 2024

The documentation of library(diag) shows how to decompile inlined predicates indicated by code offsets, using inlined_instructions/2: https://www.scryer.pl/diag

@haijinSk
Copy link
Author

haijinSk commented Oct 13, 2024

I'm sorry. I'm only a naive user, not the right person to understand things here...

?- inlined_instructions(6800, Is),maplist(portray_clause, Is).
try_me_else(66).
allocate(6).
get_variable(y(1),2).
get_level(y(2)).
get_variable(x(3),1).
put_variable(y(3),1).
put_value(y(1),2).
put_variable(y(4),4).
call('$skip_max_list',4).
cut(y(2)).
try_me_else(10).
get_prev_level(y(5)).
put_value(y(4),1).
put_constant(level(shallow),[],x(2)).
call(==,2).
cut(y(5)).
put_value(y(1),1).
put_unsafe_value(3,2).
deallocate.
execute(=,2).
retry_me_else(15).
get_prev_level(x(3)).
call(nonvar,1,y(4)).
cut(x(3)).
call(var,1,y(1)).
put_value(y(4),1).
put_list(level(shallow),x(2)).
set_void(2).
call(=,2).
put_constant(level(shallow),finite_memory,x(1)).
put_structure(/,2,x(2)).
set_constant(length).
set_constant(2).
deallocate.
execute(resource_error,2).
retry_me_else(11).
get_prev_level(x(3)).
call(nonvar,1,y(1)).
cut(x(3)).
put_variable(y(5),1).
sub(y(1),y(3),1).
call(is,2).
put_unsafe_value(4,1).
put_value(y(5),2).
deallocate.
execute(length_rundown,2).
retry_me_else(14).
get_prev_level(y(6)).
put_value(y(1),1).
put_value(y(4),2).
call(==,2).
cut(y(6)).
put_value(y(4),1).
call(failingvarskip,1).
put_constant(level(shallow),finite_memory,x(1)).
put_structure(/,2,x(2)).
set_constant(length).
set_constant(2).
deallocate.
execute(resource_error,2).
trust_me(0).
put_unsafe_value(4,1).
put_value(y(1),2).
put_unsafe_value(3,3).
deallocate.
execute(length_addendum,3).
retry_me_else(8).
call(integer,1,x(2)).
neck_cut.
put_constant(level(shallow),not_less_than_zero,x(1)).
put_structure(/,2,x(3)).
set_constant(length).
set_constant(2).
execute(domain_error,3).
trust_me(0).
put_constant(level(shallow),integer,x(1)).
put_structure(/,2,x(3)).
set_constant(length).
set_constant(2).
execute(type_error,3).
   Is = [...].

?- inlined_instructions(19275, Is),maplist(portray_clause, Is).
get_value(x(1),3).
get_variable(x(3),1).
put_value(x(2),1).
execute(no_hat_call,1).
   Is = [...].

?- inlined_instructions(19371, Is),maplist(portray_clause, Is).
allocate(2).
get_variable(y(1),2).
put_variable(y(2),2).
call(copy_term_nat,2).
put_structure(:,2,x(1)).
set_constant(lambda).
set_local_value(y(2)).
put_value(y(1),2).
deallocate.
execute(call,2).
   Is = [...].

@haijinSk
Copy link
Author

haijinSk commented Oct 13, 2024

I see... the "existence_error", like: we: the Prolog system suddenly does not see the definition/predicate; as if the system is looking for the definition in the wrong context/module...

@UWN
Copy link

UWN commented Oct 14, 2024

In some sense, your query ?- wam_instructions(test2/0,Instrutions). is all we need to know. Clearly, call/2 at the end is handled like an ordinary predicate. It just passes the term over but forgets that its first argument is a meta-predicate. So in this argument, a module qualification has to be passed further on. But there is none. And thus, in module lambda the meta-calls are resolved locally. Or that's my theory.

But then, (after consulting your original program):

?- test1.
   true.
?- test2.
   error(existence_error(procedure,length/2),length/2). % we are not happy with this
?- asserta((lambda:length(_,_):-throw(ha_gotit))).
   true.
?- test2.
   error(existence_error(procedure,length/2),length/2), unexpected.
   throw(ha_gotit). % expected, but not found
?- lambda:length(1,2).
   error(existence_error(procedure,length/2),length/2), unexpected.
?- asserta((l:length(_,_):-throw(ha_gotit))).
   true.
?- l:length(1,2).
   error(existence_error(procedure,l:length/2),length/2), unexpected.

So what is happening with these asserts??

@hurufu
Copy link
Contributor

hurufu commented Oct 14, 2024

This works as expected:

?- [user].
l:length(_,_) :- throw(ha_gotit).

?- l:length(A,B).
   throw(ha_gotit).
?- 

So it is only problem with assertions

@hurufu
Copy link
Contributor

hurufu commented Oct 14, 2024

Also after studying source code of loader.pl I can tell that correct modules are added to arguments of meta-predicates during (more-or-less) goal expansion. And code here is quite tricky and has a lot of special-cases, for example so called transitive goals which work afaik only for sequences of unifications using (=)/2, but don't work for implicit unifications (like unify(A, A)).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants