Library(yall) appears to be confusing predicate_property/2, clause/3

Ignore this – I figured out my mistake.

I’m working on protobufs.pl and hacking test_cover.pl to make sure my test cases are sufficient. None of the code directly uses library(yall), but apparently it’s getting imported indirectly.

Questions:

  1. How do I figure out what is importing library(yall)?
  2. How do I turn off the weird behavior of yall’s (/)/2 predicate?

Here are details from a session, with some comments interspersed.

$ /usr/bin/swipl
Welcome to SWI-Prolog (threaded, 64 bits, version 8.3.26)

?- [library(protobufs)].
true.

The code for write_type/5 consists of 6 facts and is here: contrib-protobufs/protobufs.pl at 42ff818ff8603b129daea44ff986de6f97ede219 · SWI-Prolog/contrib-protobufs · GitHub … I confirm this by:

?- forall(protobufs:wire_type(T,V), writeln(wiretype(T,V))).
wiretype(varint,0)
wiretype(fixed64,1)
wiretype(length_delimited,2)
wiretype(start_group,3)
wiretype(end_group,4)
wiretype(fixed32,5)
true.

So, there should be 6 results from clause/3 … but no:

?- clause(protobufs:wire_type/2, Body, Ref).
false.

So, I try to find the predicate properties. Note the following strange values:
imported_from(yall)
file(/usr/lib/swi-prolog/library/yall.pl)
number_of_clauses(1) – should be 6.
line_count(275) – discussed below

?- forall(predicate_property(protobufs:wire_type/2, P), writeln(P)).
interpreted
visible
static
imported_from(yall)
transparent
meta_predicate? / 0
file(/usr/lib/swi-prolog/library/yall.pl)
line_count(275)
number_of_clauses(1)
number_of_rules(1)
last_modified_generation(5556)
defined
size(384)
true.

And now clause/3 succeeds – although with a strange Body:

?- clause(protobufs:wire_type/2, Body, Ref).
Body =  (lambda_free(wire_type), copy_term_nat(wire_type+2, wire_type+_548), call(_548)),
Ref = <clause>(0x560e3a6f4720).

As for line_count(275): it corresponds to this: swipl-devel/yall.pl at 5772c803985cb8f88b0f96052abf8c939f8b8c23 · SWI-Prolog/swipl-devel · GitHub

'/'(Free, Lambda) :-
    lambda_free(Free),
    copy_term_nat(Free+Lambda, Free+LambdaCopy),
    call(LambdaCopy).

which is exported by library(yall).

Arrrggghhhh!!!

I yet again got confuseded by foo/2 vs foo(_,_) when looking up predicate properties.

Is there a standard way of describing these two ways of referring to predicates?

As for converting the forms: library(prolog_code) pi_head/2.

Edit: What’s especially confusing is that current_predicate/2 uses the foo/2 form but predicate_property/2 uses the foo(_,_) form.

1 Like

Not sure if this is what you’re asking, but the form foo/2 is called a “predicate indicator” (SWI, YAP) or “predicate spec” (SICStus, Ciao). foo(_,_) is usually just called a “head” or “goal”. Somewhere I’ve also read “skeletal goal” for the case where all arguments of the term are _ and not some other value.

(pi_head/2 stands for “predicate indicator/head”)

And yeah, this is always a fun pitfall. The best “solution” I know is to assume that everything expects the head term form (foo(_,_)) unless you know otherwise - because if you try to pass a head term where a predicate spec is expected, you get an error, so you notice when you did it wrong. The other way around (passing a predicate spec where a head term is expected) you silently get wrong results, as you’ve found out :slight_smile:

Maybe we need a different form of current_predicate/2 … this is what confused me:

?- current_predicate(protobufs:P), predicate_property(protobufs:P, file(File)).
P = protobuf_tag_type/4,
File = '/home/peter/src/swipl-devel/build/home/library/yall.pl' ;

whereas I should have written:

?- current_predicate(protobufs:P0), pi_head(P0,P), predicate_property(protobufs:P, file(File)).
P0 = protobuf_tag_type/4,
P = protobuf_tag_type(_5538, _5540, _5542, _5544),
File = '/home/peter/src/swipl-devel/build/home/library/protobufs.pl' 

I suppose a good type-checker would have caught this. :smiling_imp:

current_predicate/2 is actually the different form you’re looking for :slight_smile: For even more confusion, current_predicate/1 (which you use in your example code) uses predicate specs, but the 2-argument version uses head terms:

?- current_predicate(lists:PI).
PI = selectchk/3 ;
PI = append/3 ;
% ...
?- current_predicate(_, lists:Head).
Head = selectchk(_23944, _23946, _23948) ;
Head = append(_24686, _24688, _24690) ;
%...

It might not, because protobuf_tag_type/4 is technically a valid head, even if it’s a very unusual one… A linter should definitely warn about it though.