AppliedLogicSystems / ALSProlog

ALS Prolog Compiler & Development Environment
https://alsprolog.com
MIT License
16 stars 9 forks source link

Develop an appealing example for the use of %p in printf #164

Closed kenbowen closed 5 years ago

kenbowen commented 5 years ago

Develop an appealing example for the use of %p in printf. It should show how %p can do something which is more difficult to achieve without it. As a devil’s advocate approach, note that this example

?- sprintf(X, 'Answer: %p', [S^write(S,42)]). X="Answer: 42"

is easily achieved with

?- sprintf(X, 'Answer: %t', [42]). X="Answer: 42"

So to me, the problem is coming up with something relatively natural where S^Goal uses the fact that it has S in hand to compose some output which might be difficult to extract from a version of Goal not containing S and also difficult to compose out of multiple %t placeholders and constant characters. The following is artificial, but it tries to move in what might be the right direction. (However, it does show that there is a module-related problem in the implementation so far.)

Load the following predicate:

b(S,K) :- Goal = (write(S, 'h('), write(S, foo),put(S, 0',), write(S,K), put(S,0')) ), Goal.

Then:

?- sprintf(X, 'Answer: %p', [S^b(S,bar)]). Error: Operation attempted on object of type procedure which does not exist. Operation attempted on: builtins: b( stream_descriptor('\b',open,string,string(_A),[noinput|output],false, 43,[],[],0,0,true,0,wt_opts(78,400,flat),[],wait,text,eof_code, true,0), bar).

Adding a module qualifier lets it work:

?- sprintf(X, 'Answer: %p', [S^(user:b(S,bar))]).

X="Answer: h(foo,bar)" S=stream_descriptor('',closed,string,string("Answer: h(foo,bar)"), [noinput|output],false,44,"Answer: h(foo,bar)",")",0,0,true,0, wt_opts(78,400,flat),[],wait,text,eof_code,true,0)

Note: related emails are included below.

6/12/19: Chuck -> Ken

The old and new doc and examples for %p don't quite capture how %p is intended for use (assuming my understanding is correct). The most important thing to convey is that the S in S^Goal is a mechanism for printf to pass the output stream down to the Goal. Prolog doesn't have first-class anonymous functions (lambdas), so S^Goal is the pseudo-equivalent of "function(S) { Goal }" in Javascript.

I think the following is a good example that gets to the heart of %p. Because it uses sprintf, it makes it clear that the output stream is ephemeral and unknown (created by sprintf on the fly). Note how S must be unbound -- arguable passing a bound S for %p should result in a domain-error:

?- sprintf(X, 'Answer: %p', [S^write(S,42)]). X="Answer: 42" S=stream_descriptor(...)

[ Note, it seems like a bug that S gets bound to a stream as a result. ]

Also, I don't understand the utility of allowing a plain Goal for %p. For example, one gets non-sensical results like:

?- sprintf(X, 'Answer: %p', [write(42)]). 42 X="Answer: "

Is there any case where the plain Goal form is useful (specifically excluding "accidental" usefulness of outputting to user_output)?

6/12/19: Ken -> Chuck

First:

?- open(string(Y), write, S), printf(S, 'Answer: %p', [S^write(S,42)]),close(S).

Y="Answer: 42" S=stream_descriptor('',closed,string,string("Answer: 42"),[noinput|output], false,45,"Answer: 42","2",0,0,true,0,wt_opts(78,400,flat),[],wait,text, eof_code,true,0)

yes.

And

?- current_output(S), printf(S, 'Answer: %p', [S^write(S,42)]). Answer: 42 S=stream_descriptor('\002',open,console,'standard output',[noinput|output], false,-2,0,0,0,0,true,0,wt_opts(78,400,flat),[],wait,text,eof_code,true,0)

yes.

Second, the relevant clause in blt_io.pro (line 102ff) is:

%%%% %p -- call print procedure as argument printf0([0'%,0'p |Format], Stream, [PrintGoal|ArgList],Options) :-!, (PrintGoal = Stream^PrintGoal0 -> call(PrintGoal0) ; (PrintGoal = [Stream,Options]^PrintGoal0 -> call(PrintGoal0) ; call(PrintGoal) )

   ),
   printf0(Format,Stream,ArgList,Options).

Note that the Stream in Stream^PrintGoal0 must unify with the toplevel Stream argument in printf0.

Third, the following line (after the pseudo-code for %p, just before sprintf/3) says that the Stream occurring in Stream^PrintGoal0 should be the same as the output stream of print/3,4:

It is important that Stream above is the stream argument to print/3 or printf/4.

I do think this is saying what you said with

S in S^Goal is a mechanism for printf to pass the output stream down to the Goal.

chuckhoupt commented 5 years ago

Are the examples added in 55cd00b4397cee9fc97d0702afc1fb033acb68c6 appealing enough?

kenbowen commented 5 years ago

Yes. I thought I said so. Sorry. —Ken

On Jul 11, 2019, at 5:46 AM, Chuck Houpt notifications@github.com wrote:

Are the examples added in 55cd00b appealing enough?

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub, or mute the thread.

chuckhoupt commented 5 years ago

Roger. I was just asking if the issue could be closed. I've closed it.