Dear community, consider the following code snippet:
File: ~/Desktop/simplemod.pl
:- module(simplemod, [say_hi/0]).
say_hi :- writeln('Hi from simplemod on Desktop!').
When using predicate_property/2 to check for exported status, I get an unexpected fail, like so:
?- cd('~/Desktop').
true.
?- [simplemod].
true.
?- use_module(simplemod, [say_hi/0]).
true.
?- say_hi.
Hi from simplemod on Desktop!
true.
?- predicate_property(simplemod:say_hi/0, exported).
false.
In other words, the call to say_hi/0 does what it’s supposed to do, but the introspection by predicate_property/2 seems to report that it shouldn’t.
This can’t be correct, no? I read the documentation, and there are a few older, related posts here, but could not find an answer to this specific issue.
Is this a (known) bug?
Is it a misunderstanding on my part?
(ps: no urgency, my code runs fine with a work around, just wanted to report the issue, fyi)
It should be the predicate head, not the predicate functor… I also got bamboozled at first
?- predicate_property(simplemod:say_hi, P).
P = interpreted ;
P = visible ;
P = exported ;
P = static ;
P = file('simplemod.pl') ;
P = line_count(3) ;
P = number_of_clauses(1) ;
P = number_of_rules(1) ;
P = last_modified_generation(9356) ;
P = defined ;
P = size(328) ;
false.
For homework, figure out what you get with predicate_property(say_hi/0, P) and what it means.
Thank you, Boris. This helped a lot! From comparing the two outputs I think I can see what’s happening:
?- predicate_property(simplemod:say_hi, P).
P = interpreted ;
P = visible ;
P = exported ;
P = static ;
P = file('/Users/maartenstol/Desktop/simplemod.pl') ;
P = line_count(4) ;
P = number_of_clauses(1) ;
P = number_of_rules(1) ;
P = last_modified_generation(6185) ;
P = defined ;
P = size(328).
?- predicate_property(simplemod:say_hi/0, P).
P = interpreted ;
P = visible ;
P = static ;
P = imported_from(yall) ;
P = transparent ;
P = (meta_predicate? / 0) ;
P = file('/Applications/SWI-Prolog.app/Contents/swipl/library/yall.pl') ;
P = line_count(275) ;
P = number_of_clauses(1) ;
P = number_of_rules(1) ;
P = last_modified_generation(6210) ;
P = defined ;
P = size(392).
If I understand correctly, it’s like this: when using say_hi/0 as an argument to predicate property then it’s parsing say_hi/0 as the compound term /(say_hi, 0), ignoring my own definition of say_hi.
The give aways are that when using say_hi/0 we get things like P = imported_from(yall), P = transparent, and P = (meta_predicate? / 0). I guess it’s now looking at the division operator \. Anyway, not my say_hi function.
Would there be a way to not autoload library(yall) in such cases? I can already get a warning with the correct flag but I am not sure what to do about it:
?- set_prolog_flag(warn_autoload, true).
true.
?- predicate_property(say_hi/0, P).
Warning: Auto-loading (/)/2 from library(yall) into module user is deprecated due to term- or goal-expansion
P = interpreted .
I now added the following to ~/.config/swi-prolog/init.pl:
:- set_prolog_flag(warn_autoload, true).
@martz , with this line in my initialization file I now get a warning when I mistakenly use the functor instead of the head and library(yall) hallucinates a lambda definition:
$ swipl -q simplemod.pl
?- predicate_property(say_hi/0, P).
Warning: Auto-loading (/)/2 from library(yall) into module user is deprecated due to term- or goal-expansion
P = interpreted .
I see, indeed the warn_autoload flag gives a bit more information than the false I got from the call to predicate_property.
I can use this, thanks for this idea!
I’m trying to understand this:
Conversely, does the content of the warning: Auto-loading (/)/2 from library(yall) into module user is deprecated due to term- or goal-expansion imply that the functor vs head mistake must be its cause?
I’m not sure it does, as I’m not sure what the warning means to say with …is deprecated due to term- or goal-expansion
I think the warning is there because if yall is auto-loaded, it doesn’t install the term/goal-expansion hooks that compiles the lambdas into auxiliary predicates and is much slower. You can see the difference by using listing/1 to see the body of a predicate that uses a yall lambda with and without explicitly use_module-ing yall.:
In the first case, the call to the lambda is interpreted, while in the second it’s made the __aux_yall.... and everything is (should be?) as fast as if you weren’t using lambdas!
Not really. The cause of this confusion is twofold: the two different ways to refer to a predicate, i.e., Name/Arity vs Name(Arg1, …) and the existence of a predicate (/)/2 (or /(_,_). The first is history and has some rationale. Writing Name/Arity is concise and readable. If you gave a goal term and you want to know what you can do with it though, it is cumbersome to have to create a predicate indicator for it first. If we consider applications such as a meta interpreter or static analysis tool, they first have a goal term. So, predicate_property(Head, Property) is much easier.
The introduction of (/)/2 as a predicate in library(yall) completed the confusion Before, predicate_property(Name/Arity, …) was always false and we could even have considered raising a type error. Now it is true if this library is (auto-)loaded, but with different properties than you expect.
The autoload warning was added because both performance and semantics of library(yall) is affected by having it being autoloaded or not. In theory, this dependency can exist for all libraries that define macro expansion. The warning is still not the default as it results in too many bogus warnings. I still do not know how to improve on that This is also connected to dependency tracking.
Thank you Jan, I think I’m beginning to understand now.
And indeed, my toy problem here originated from a dependency tracking issue giving weird results. I understand how historical contingencies can lead to these hard to resolve situations.
On the other hand, this issue did make me look into yall and prolog lambda expressions for the first time @jamesnvc thanks for your example, I had no idea this could happen, nor exactly why it happens, still tbh.
Thanks for the detailed replies everyone! My code runs better, and I got some new insights