Why does the terminal leave a line between % d1 and % d2 even if everything is just buffered?
Could it be something about teaminals and streams I don’t understand or is it something related to Prolog? (debug/3 writes to user_error, format/2 and ansi_format/3 write to current_output)
If this is not the right place to ask this, please point me to where I can learn about this interaction.
Thanks
Edit: Changed the category to Help! as I think this fits better
It is indeed about the streams. debug/3 writes to user_error, format/2 to current_output, which is normally user_output. user_error is unbuffered, while user_output is line buffered (when talking to a terminal; fully buffered otherwise). So, the debug/3 write immediately. the baz is buffered until the ~n. To get what you want, use flush_output/1 or change user_output to be unbuffered using set_stream/2.
So debug/3 writes to user_error which is unbuffered (and if I see correctly, it also terminates with an newline so the unbuffered nature doesn’t really make a difference).
But as format/2 is buffered entirely and nothing is emitted to the terminal,
why doesn’t the terminal just print the two debug statements below each other like normal?
When I change the debug/3 to format/3 (to user_error):
debug/3 uses format(user_error, '~N<your format>~n', Args), a little simplified. There is a dubious feature that writing unbuffered output does update the notion of the current column, so ~N inserts a newline. In most cases doing the column arithmetic before the buffering is correct, but user_output and user_error normally share the same position storage and flush independently.
I wonder whether it could be a solution to make writing to user_error flush user_output first. That would fix these cases. What would go wrong?
Ah that’s what I was missing, thank you very much.
I personally think that always flushing user_output would not always be practical behavior as it could break outputs that buffer intentionally even when user_error is not routed to the same terminal as user_output.
But some documentation for this behavior would be good for everyone after me wondering about this.
It gets a very complicated story The implementation is basically wrong as it merges the position of all three “standard” streams. That is fine if this is a physical device, but wrong if some of these streams are not connected to the same physical device. I doubt there is a portable way to figure out what is connected to the streams 0,1,2 …
Note that reading user_input also flushes user_output, so some interaction is already there …
If there is some de-facto acknowledged way of doing position sharing and flush coordination between the standard streams, please share. Given how ill it is currently defined, documentation seems a bad idea …
Interleaved stderr and stdout (with various flavours of buffering) have been a problem for Unix developers since the beginning. If somebody had discovered/invented a solution, I’m sure it would be in common use (the only one I know is 2>&1 on the command line; and that has its own problems).
Having debug/3 affect user_output is likely to produce some other unexpected behaviour.
Instead, I would suggest having a user:debug_hook/3 that is called first and if it fails, the current behaviour is used. It could do a flush of user_output then fail, to get the results you suggest, but in a user-controlled manner.
I don’t particularly like having lots of hooks (e.g., portray/1 requires some care) but this is something that can be put in init.pl, together with setting debugger_write_options, etc.
You can already do that. debug/3 uses print_message/2, which calls user:message_hook/3. All in all, I think there is no clean universal solution and users will have to live with it, calling flush_output/1 when there are synchronization issues between the two streams.
On the other hand, the implication of flushing user_output before user_error if both are connected to a terminal is probably fairly harmless and may reduce confusion. I’m willing to give it a try. I guess I’m a little more willing to take risks than some other developers