[racket] Behaviour of gen:custom-write method

From: Hamish Ivey-Law (hamish.ivey.law at gmail.com)
Date: Sat May 17 14:44:48 EDT 2014

Dear Racket-users

I am trying to understand the behaviour of the `gen:custom-write`
method. What I find confusing is that the `write-proc` function is
called twice for explicit calls to `write`, `display` or `print` and
three times when display happens in the (x)repl.  For example:

-> (struct thing (n)
           #:methods gen:custom-write
           [(define (write-proc tg port mode)
              (printf "In thing write-proc (port = ~a, mode = ~a)~%" port mode)
              (fprintf port "Thing is ~a~%" (thing-n tg)))])
-> (define tg (thing 7))
-> tg
In thing write-proc (port = #<output-port:null>, mode = #f)
In thing write-proc (port = #<output-port:null>, mode = #f)
In thing write-proc (port = #<printing-port>, mode = 0)
Thing is 7

If the printing mode is specified explicitly by using `write`,
`display` or `print`, then `write-proc` is still called twice:

-> (write tg)
In thing write-proc (port = #<output-port:null>, mode = #t)
In thing write-proc (port = #<output-port:redirect>, mode = #t)
Thing is 7
-> (display tg)
In thing write-proc (port = #<output-port:null>, mode = #f)
In thing write-proc (port = #<output-port:redirect>, mode = #f)
Thing is 7
-> (print tg)
In thing write-proc (port = #<output-port:null>, mode = #t)
In thing write-proc (port = #<output-port:redirect>, mode = 0)
Thing is 7

Is there an example of an implementation of `write-proc` which shows
how one should handle the combinations of values of `port` and `mode`
(and whatever else) in such a way that, for example, we can avoid
potentially expensive conversions to strings which are later
discarded?  Or are we simply expected to cache anything that we don't
want to recompute ourselves?  What is the rationale behind calling
`write-proc` several times?  Where is this behaviour documented (other
than in the Racket source, obviously)?

(In my particular application, the analogue of `n` is gigantic *and*
must undergo a costly conversion to a string via FFI before being
printed to a file.  Not something I can afford to do twice!)

I am very new to Racket.  Please keep that in mind when responding!
Thanks very much in advance!

Kind regards,
Hamish.

Posted on the users mailing list.