Can `parameterize` work with pretty printing?

28 views
Skip to first unread message

Ryan Kramer

unread,
Jan 3, 2020, 2:44:01 PM1/3/20
to Racket Users
I am `gen:custom-write` and `make-constructor-style-printer` on an object graph that may contain cycles. Within my custom write procedure, I use `parameterize` to register an object into dictionary and later in the writing process I can check that dictionary, detect the cycle, and do more custom write logic.

But I have noticed that sometimes that my `parameterize` has gone out of scope by the time the printer gets deeper into the object graph. I was able to work around this issue by using another port, then copying the result to the real port like this:

(define-syntax-rule (write-now proc x port mode)
  (let* ([alt-port (open-output-string)]
         [_ (proc x alt-port mode)]
         [str (get-output-string alt-port)])
    (write-string str port)))

This is better than just doing `(proc x port mode)` because it always prints the string that I expect. However, it breaks pretty printing. I've noticed that the built-in pretty printing logic will sometimes hit the same leaf object multiple times, as if it is testing to see how wide the result is and backing up and trying again. Perhaps it can also delay evaluation? That would explain why my `parameterize` has ended.

Is there a way to use `parameterize` within custom write logic and keep pretty printing working? Is there a better approach I should take?



Additional Information
===
The `write-now` macro, as written, is "correct but unpretty". I can change it to "incorrect but pretty" as follows:

(define-syntax-rule (write-now proc x port mode)
  (proc x port mode))


This is a test that fails in "incorrect but pretty" mode. Rackunit reports:

location:   from.rkt:373:2
actual:
  "(from #0=a/0 \"A\" (attach #1=b/1 \"B\" (attach #2=c/2 \"C\") #3=(list #0# #1# #2#)))"
expected:
  "(from a/0 \"A\" (attach b/1 \"B\" (attach c/2 \"C\") (list a/0 b/1 c/2)))"


Here are two screenshots, one pretty and one unpretty:

pretty.png

unpretty.png


Ryan Kramer

unread,
Jan 3, 2020, 10:15:48 PM1/3/20
to Racket Users
I found `make-tentative-pretty-print-output-port` and `tentative-pretty-print-port-transfer` which looks like it might be the right way to go. I changed `write-now` as follows:

(define (write-now proc x port mode)
  (if (pretty-printing)
      (let* ([alt-port (make-tentative-pretty-print-output-port port
                                                                (pretty-print-columns)
                                                                (λ () (void)))]
             [_ (proc x alt-port mode)])
        (tentative-pretty-print-port-transfer alt-port port))

      (let* ([alt-port (open-output-string)]
             [_ (proc x alt-port mode)]
             [str (get-output-string alt-port)])
        (write-string str port))))

And it looks like it mostly works, except that now it seems to ignore my `prop:custom-print-quotable 'never`, or else I am missing it somewhere... I'll investigate more.


But I don't understand how the `overflow-thunk` of `make-tentative-pretty-print-output-port` should be used. From the docs: "The overflow-thunk procedure is called if more than width items are printed to the port or if a newline is printed to the port via pretty-print-newline; it can escape from the recursive print through a continuation as a shortcut, but overflow-thunk can also return, in which case it is called every time afterward that additional output is written to the port."

Does that mean its return value is ignored? It seems like it. And when (if ever) should I call `tentative-pretty-print-port-cancel` instead of `tentative-pretty-print-port-transfer`?
Reply all
Reply to author
Forward
0 new messages