Document listing/1
fix #2923
Thank you a lot for working on this!
I think an important point to state in the documentation is that the predicate must be public so that its body can be inspected at all. One way to make a predicate public is to declare it as dynamic, using the dynamic/1 directive.
A predicate without clauses that has been declared as dynamic must not fail in listing. That is the behavior in SICStus.
That’s what the current predicate does so I was documenting per implementation.
It would indeed be a good idea to align the behavior with other systems. ~~I think that, e.g. SWI prints a message for private procedures~~.
edit: I misremembered. SWI succeeds but shows a comment-like message for procedures implemented in native code. I'm not sure if SWI even has private procedures.
?- listing(writeq/1).
% Foreign: system:writeq/1
true.
I don't love this, but it seems better than an error if you try to list a whole module that re-exports a private procedure.
Also note that author intends to "apply Cunnigham's law" that is to produce incorrect answers on purpose!
Believe me, I’m quite good at being wrong without trying to!
A predicate without clauses that has been declared as dynamic must not fail in listing. That is the behavior in SICStus.
I can't find this in the SICStus documentation and don't have a license to try it myself.
What's the behavior in SICStus's listing/1 so I can correct the implementation and the documentation?
- When given a PI that does not name a procedure, does it fail silently (like scryer) or with an error (like SWI)?
- With a dynamic procedure with no clauses, does it print anything or just succeed silently?
@triska, @UWN I'm blocked on how to proceed. How should I fix the behavior? Or should I leave the no-clauses case undocumented?
You can get a free evaluation licence of SICStus!
A predicate that fails can be easily emitted as Head :- false..
A predicate that fails is similar but not identical to a predicate with no clauses.
Should listing/1 print such a "virtual" clause, a comment, or nothing when there truly are no clauses?
It seems that, on SICStus, the behavior is substantially different. It doesn't print anything for a dynamic predicate with no clauses, and even succeeds when called on a built-in predicate!
| ?- listing(foo/0).
yes
| ?- listing(true/0).
* listing(user:true/0) - no matching predicate
yes
A predicate without clauses that has been declared as dynamic must not fail in listing. That is the behavior in SICStus.
Expecting the behavior of SICStus seems to be setting yourself up for disappointment.
Many errors are issued in SICStus via messages. That is warnings. That's what you got. Scryer is more restrictive issuing directly errors.
Many errors are issued in SICStus via messages. That is warnings. That's what you got. Scryer is more restrictive issuing directly errors.
If I'm understanding correctly, the purpose of listing/1 seems to be either:
- (a) interactively, to help a human understand whatever it can about a given procedure
- (b) to convert the procedure to a form that can be fed into another database at a later time
@triska, @uwn, which is closer to the vision you have in mind for what listing/1 should be?
These use cases imply different, conflicting behaviors and I think that's why this method NEEDS documentation. I'll be happy to rewrite the procedure and the documentation to match.
IMHO listing/1 documentation should also include goal and term expansion into consideration and how it affects the output.
For example:
:- dynamic(a/2).
a --> "a".
And then the query:
?- listing(a/2).
a(A,B) :-
A=[a|B].
true.
@hurufu Don't quote me on this, but I think the intent is to be able to declare such a nonterminal with :- dynamic(a//0) and query it with listing(a//0) though currently this produces the identical output as listing(a/2).
Anyway, what's your desired output (even if it might be impossible to make happen at this time)?
Current output:
a(A,B) :-
A=[a|B].
true.
1:
a --> "a".
2:
a(A,B) :-
A=[a|B].
% expanded from:
% a --> "a".
3:
a --> "a".
% expands to:
% a(A,B) :-
% A=[a|B].
% true.
Would you expect to see the :- dynamic(...), :- metapredicate(...) etc. declarations in the printed output as well?
@rotu I would vote for 1st variant, but my comment wasn't about expected behavior, but about that listing/1 documentation should contain a note that current implementation reports clauses after goal and term expansions, so that novices wouldn't get surprised when using DCG or CLPZ.
Alright. This PR is done and ready to go. I didn't document the interaction with goal- and term-expansion since I don't understand them myself.
@hurufu, I'm not sure how goal- and term-expansion actually work and how they're supposed to interact with listing and with the calling context. My impression was that listing should reflect term-expansion but not goal-expansion. PLEASE follow-up with a PR that adds these details!