expose function to flush formatter's internal queue#506
expose function to flush formatter's internal queue#506seliopou wants to merge 2 commits intoocaml:trunkfrom inhabitedtype:trunk
Conversation
Previous to this commit the Format API did not allow the user to flush a formatter's internal queue without also flushing the formatter's output stream. This was a problem for custom formatters that may have been used in conjunction with kfprintf or ikfprintf. When the continuation was called, the formatter's queue of operations may not have been flushed. The only way that the user could force a queue flush was to call pp_print_flush, forcing the user to introduce a blocking call before it was necessary, regardless of other buffer state within the system. Exposing the internal queue flushing operation solves this problem. Names may be changed to protect aesthetic sensibilities.
|
Additional motivation for this patch: without this function, one cannot define |
Courtesy of @Drup, with minor edits.
|
Very nice, thanks. |
|
Merged in trunk ( 33cf3f1 ), thanks! |
|
@Drup I do not agree with your arguments:
|
|
@seliopou I cannot grasp your motivation here: you never need to flush the formatter when using ikprintf or kfprintf: the continuation gets the formatter with all the necessary material; hence, you just have to go on printing on the formatter and everything will go smoothly! On the contrary, flushing the queue will just ensure that you do not want to interact with the preceding printing material and boxes structure. To summarize: flushing the pretty printer queue was left private to the module on purpose. It would be confusing to use this facility, since it does not interact properly with the pretty-printing strategy (all of a sudden all currently opened boxes are closed and everything is printed instead of letting the pretty-printer do its job). The print_flush facility was simply added for interactive usage: when asking a question to the user you absolutely need the entire question to be displayed on the output device of the pretty-printer, hence the automatic flushing of the underlying output device. |
|
@pierreweis I believe you are making assumptions about the underlying output stream attached to the formatter, in addition to assumptions about usage patterns (i.e, "go on printing"). Without this API, custom formatters that have not yet flushed their queues and are guaranteed to never receive further input will never flush their queues, except for perhaps at the end of the program. In a server application, programs don't terminate at the end of a request, so there will be no way of receiving formatter's output. I cannot speak to the justification for this operation previously being private, nor reconcile that with why |
|
Thank you for your answer.
Lot of questions: if you use kfprintf, it means you want the get the I'm sorry but I still do not understand your use case: could you define what you In any case, if you are using the new function to flush the queue then the Flushing the queue without flushing the device is just a source of eroor in
I always used to support user's concerns as much as possible. I'm still doing So please, let me understand why this error prone functionality is in fact Regards, Pierre Weis INRIA Paris, http://bat8.inria.fr/~weis/ |
| [print_string] is equal to [pp_print_string std_formatter]. *) | ||
|
|
||
| val pp_flush_formatter : formatter -> unit | ||
| (** [pp_flush_formatter fmt] flushes [fmt]'s internal queue, ensuring that all |
There was a problem hiding this comment.
The fmt here should be ppf to be consistent with the rest of the module's documentation.
There was a problem hiding this comment.
(nevermind it has been merged...)
|
It appears that I merged the PR a bit too soon, as @pierreweis, the maintainer of the Format module, has objections. I'm thinking of reverting the merge and letting you ( @pierreweis , @seliopou , @Drup ) come to a consensus. |
The use of
A custom formatter is the result of a call to let fprintf format =
let tref = ref noop in
let formatter =
Format.make_formatter
(fun str pos len -> tref := !tref ^^ string ~pos ~len str)
(fun () -> tref := !tref ^^ flush)
in
let k fmt =
Format.pp_flush_formatter fmt ();
!tref
in
Format.kfprintf k formatter formatThe format string is used as an edsl, producing a data structure that represents the writes and flushes that must be performed. The result can be composed with other write and flushed operations (some of which are defined via generator functions). Additional library code can then traverse the representation, scheduling writes and flushes as defined by the user. Without a call to
I don't think this is right in general. This is where an assumption about the underlying output stream (or device or concept or whathaveyou) comes into play. If the output is buffered, then your situation may arise. If the write handler of the formatter (first argument of All of this is possible before these commits were merged. In addition to that, a user that defined a custom formatter may have incorrectly believed (as I did) that the write and flush callbacks would have been called after calls to In my use-case, I want to flush the internal formatter queue so I can get bytes that need to be written output stream, so I can schedule the writes. This is independent from flushing the output stream, which will block until previous writes complete. (Or at least, it's a possibility that the operation will block.) To separate those two concerns—getting bytes to schedule for writing and blocking until previous writes complete—in a custom formatter requires that formatter's underlying queue can be flushed. This is important in order to perform efficient i/o, which I won't get into further here. |
I do not understand what you mean by
As the name indicates, the result of ``make_formatter'' is simply named a
Variables noop, flush, string are unbound in this piece of code. However, it seems that you typically mimmick a regular formatter that prints on May I also suggest that repeated format concatenation is not efficient ? This
Did you mean that you are parsing the resulting format strings or even
To be clear: The new functionality, Format.pp_print_queue would do exactly the same, So: if the underlying device never need to be flushed the two functions Oth: if the underlying output device of the formatter needs to be flushed, Hence: using pp_print_flush is always safer than using pp_print_queue. Question: what use case needs to use pp_print_queue instead of
I do not understant what you mean by ``the write call back'': there is no I do not either understand how you could imagine that a call to fprintf would (Consider for instance a polymorphic routine that prints a pair: let print_pair pa pb ppf (x, y) = Printers pa and pb are likely to use Format.fprintf: if you imagine that pa In the same vein, the need to flush the pretty-printer to get all the printed Rule of thumb for casual users of this library:
And also: In case of interactive use, the system closes all opened boxes and Last, but not least: Warning: the material output by the following functions is delayed I agree that this may still be unclear; in this case, I would be glad to
Then you definitely need to use a formatter writing to a Buffer.t output
You should separate the two concerns: get the pretty-printed material bytes Using a formater writing to a Buffer.t and using pp_print_flush asnwers the
I will not either. Regards, Pierre Weis INRIA Paris, http://bat8.inria.fr/~weis/ |
|
My understanding of @seliopou's setting is the following:
I can't comment on the performance aspects of the discussion, but to me this seems to be a reasonable use-case for making a distinction between those two notions of flushing. If I understand correctly, what is needed here is a "soft flush" that closes all boxes and perform the remaining formatting actions, without explicitly calling a "hard" flush on the underlying device. |
This is a very good summary of the issue and why @seliopou implemented this in the first place (after a discussion with me). Also, I would like to point out that Unless there is another way of implementing soft flushing, I think this function should be kept as it's currently implemented. |
|
I'm still not convinced by the new pp_print_queue functionality, but the I propose to add a new facility to support the use case directly within This message is long: the short version is: A: Adding a facility to produce some abstract data instead of printing, B: Adding pp_print_queue is easy, but we refrained to do so for years, since C: Use-case: You do not need pp_print_queue to produce the abstract syntax: D: Use-case: fprintf flushes the pretty-printer queue at each call: this This message is long: the long version is: A: design a formatter to pretty-print in an abstract way Abstract printing, i.e. producing some data structure instead of printing to This was ambitious and complex and was never implemented, We could now To provide this facility, we just have to design a type specification of the Using a formatter will be conservative to keep the compositionality and B: pp_print_queue is a bad idea I already mentioned that pp_print_queue would have a bizarre functionality: C: use-case: use pp_print_flush to produce the abstract representation If your special-purpose pretty-printer produces an abstract representation of To be clear, using pp_print_flush on the formatter will ensure that the D: use-case: flushing the pretty-printer at each call is a bad idea @seliopou: your special purpose fprintf function flushes the pretty printer For instance, the special-purpose fprintf function cannot support multiple Also, this behavior forbids composition of pretty-printing routines using it. This ``each call flushes'' behavior was precisely the motivation to deprecate |
|
@pierreweis if you'll notice, my If the need is clear, and the behavior of the function can be properly documented, what's the problem? Users don't read documentation and misuse APIs all the time. That's not a reason to not write the API to begin with. And practically speaking, if this function is included, I imagine it will go mostly ignored by casual users. |
|
I carefully read your code and I understand it. I'm sorry to insist I refuse to introduce a function that is useless and error prone. I know that users may misuse APIs and that's exactly the reason why I do not As I said, I prefer to introduce a way to print abstractly: this feature is
|
|
@seliopou @pierreweis There seems to be stalemate on this pull request. What is going to happen to it? |
|
@mshinwell The goal was to provide the needed tools in PR #615. There have been some interesting comments there but not from @seliopou. |
|
#615 has been merged, so I think this can be closed. |
Replace `Op_val` with `Field`
64235a3 flambda-backend: Change Float.nan from sNaN to qNaN (ocaml#466) 14a8e27 flambda-backend: Track GC work for all managed bigarray allocations (upstream 11022) (ocaml#569) c3cda96 flambda-backend: Add two new methods to targetint for dwarf (ocaml#560) e6f1fed flambda-backend: Handle arithmetic overflow in select_addr (ocaml#570) dab7209 flambda-backend: Add Target_system to ocaml/utils (ocaml#542) 82d5044 flambda-backend: Enhance numbers.ml with more primitive types (ocaml#544) 216be99 flambda-backend: Fix flambda_o3 and flambda_oclassic attributes (ocaml#536) 4b56e07 flambda-backend: Test naked pointer root handling (ocaml#550) 40d69ce flambda-backend: Stop local function optimisation from moving code into function bodies; opaque_identity fixes for class compilation (ocaml#537) f08ae58 flambda-backend: Implemented inlining history and use it inside inlining reports (ocaml#365) ac496bf flambda-backend: Disable the local keyword in typing (ocaml#540) 7d46712 flambda-backend: Bugfix for Typedtree generation of arrow types (ocaml#539) 61a7b47 flambda-backend: Insert missing page table check in roots_nat.c (ocaml#541) 323bd36 flambda-backend: Compiler error when -disable-all-extensions and -extension are used (ocaml#534) d8956b0 flambda-backend: Persistent environment and reproducibility (ocaml#533) 4a0c89f flambda-backend: Revert "Revert bswap PRs (480 and 482)" (ocaml#506) 7803705 flambda-backend: Cause a C warning when CAMLreturn is missing in C stubs. (ocaml#376) 6199db5 flambda-backend: Improve unboxing during cmm for Flambda (ocaml#295) 96b9e1b flambda-backend: Print diagnostics at runtime for Invalid (ocaml#530) 42ab88e flambda-backend: Disable bytecode compilers in ocamltest (ocaml#504) 58c72d5 flambda-backend: Backport ocaml#10595 from upstream/trunk (ocaml#471) 1010539 flambda-backend: Use C++ name mangling convention (ocaml#483) 81881bb flambda-backend: Local allocation test no longer relies on lifting (ocaml#525) f5c4719 flambda-backend: Fix an assertion in Closure that breaks probes (ocaml#505) c2cf2b2 flambda-backend: Add some missing command line arguments to ocamlnat (ocaml#499) git-subtree-dir: ocaml git-subtree-split: 64235a3
Previous to this commit the Format API did not allow the user to flush a formatter's internal queue without also flushing the formatter's output stream. This was a problem for custom formatters that may have been used in conjunction with
kfprintforikfprintf. When the continuation was called, the formatter's queue of operations may not have been flushed. The only way that the user could force a queue flush was to callpp_print_flush, forcing the user to introduce a blocking call before it was necessary, regardless of other buffer state within the system.Exposing the internal queue flushing operation solves this problem.
Names may be changed to protect aesthetic sensibilities.