Feedback for glossary entry on formatting/print/write/display

I'm currently preparing the Racket Glossary entry for formatting and showing Racket values. My current notes are here. (The title for the entry may change; but I'm not yet sure to what. :wink: .)

I've read most of the content in these documents:

I have a few questions/remarks on what I read so far. I suppose none of these are essential for writing the glossary entry, but I'm curious, especially regarding the first of the following issues.

Confusion about read and write

8.3 Reading and Writing Racket Data says on write:

write, which prints a value in such a way that read on the output produces the value back

As a simple example, I noticed that (write 'foo) emits foo, not 'foo, as I had expected. I thought the "reader" is the code that parses Racket (#lang racket) code in files and in the REPL, but then the output should be 'foo. If read isn't intended for what I expected, what is its role? On the other hand, what is the corresponding infrastructure/function(s) that would translate the input string "'foo" to the Racket data 'foo?

"Quoting depth" and "quotable"

This paragraph has some information on the terms "quoting depth" and "quotable", but honestly that doesn't tell me anything about the actual meaning or purpose of these concepts. :slight_smile: I thought it might have something to do with pretty-printing and experimented with the quoting depth argument of print in the REPL, but didn't see any differences in the output, regardless of using quoting-depth 0 or 1.

What do "quotable" and "quoting depth" actually mean? Can you show examples where different quoting-depth arguments in print lead to different outputs?

Parameters for read and print

I noticed that there are many parameters for the read and print (starting here and here, respectively). I wonder if all these parameters are actually needed and used?

Title for the glossary entry

I'd like to have information on the following in the glossary:

  • print, write, display functions
  • format and printf functions
  • placeholders like ~a and ~v in format and printf
  • functions like ~a, ~v and ~r

At the moment, I tend to put all of this in one entry, possible with subheadings as in String, character, byte string, because these concepts/functions are so closely related. Now I wonder what the title for this entry could be. :slight_smile: Maybe just stick with "Formatting", with other terms, for example "Display", linking to the entry?

Other feedback

In addition to the notes that I linked above, is there anything you think I should include in the glossary entry? Remember that the target audience of the glossary are readers which are relatively new to Racket and may be confused by Racket's many concepts, so entries in the glossary are intended as an overview/starting point.

I think of quote as a ' followed by the written representation of a value, produces that value.
So the written written representation of symbol with the name foo is foo and to produce that symbol I write 'foo.

It is perhaps more clear when lists, are involved: We write '(foo bar) not '('foo 'bar).

1 Like

The quote isn't needed by read:

(with-input-from-string "foo" read) ; returns the symbol foo

It seems that (in my mental model) read "adds" a quote, but I don't understand the reasoning behind that.

(with-input-from-string "'foo" read)

results in

''foo

so it's not that the quote in the input is just optional.

'foo is sugar for (quote foo), and is read as a list. Lists are one of the things the default printer quotes (Controlled by the print-as-expression parameter).

> (list? (with-input-from-string "'foo" read))
#t
> (writeln (with-input-from-string "'foo" read)) 
'foo
> (println (with-input-from-string "'foo" read))
''foo
> (parameterize ([print-as-expression #f]) (println (with-input-from-string "'foo" read)))
'foo

read isn't adding anything; it's the printer that is.

2 Likes

I think it actually becomes clearer with a "normal" expression instead of a symbol:

(with-input-from-string "(+ 3 4)" read)

results in

'(+ 3 4)

which makes sense. If the code is (+ 3 4) and I want to read it as such (but not evaluate it), the result must be '(+ 3 4).

The distinction between print, write, and display was quite mysterious for me as I was learning Racket (and sometimes I still forget which is which...!).

The most helpful thing for me so far has been reviewing the Reading and Writing Racket Data guide page, especially the outline explaining each type and the table of examples to get a feel for each one in use.

1 Like

Yes, I found that helpful, too (and mentioned the link above :slight_smile: ). Likely, I'll use a similar table in the glossary entry, but maybe with additional information. I'm still thinking about how to present the concepts. :slight_smile:

Here is a different way to think about how this works:

  • evaluating (with-input-from-string "foo" read) returns an internal data structure, a Scheme symbol type, whose name is foo
  • you could pass this Scheme symbol to eval, who will return the value associated with the symbol (or raise an exception if the symbol foo is not bound)
  • you could also pass this internal structure to print which will create a text representation of this, which is the text 'foo, which is what you see in the Racket console.

Why does print prepend a quote to the symbol name when it is asked to print a symbol? Because print assumes the text will be an input to a read-eval-print loop. Here is how that would work:

  • user types the text 'foo at the prompt
  • read reads this text as (quote foo) (a list whose first element is the symbol quote and the second element is the symbol foo
  • the (quote foo) list is passed to eval which evaluates it, producing the Scheme symbol whose name is foo
  • This Scheme symbol is than passed to print which prints it out as the text 'foo

Alex.

3 Likes

A different way of phrasing Alex's excellent explanation is:

  • write is intended to round-trip with read. That is, for (some) data v, the following code should produce v again: (with-input-from-string (~s v) read)
  • print is intended to round-trip with eval (although this round-trip is lower-fidelity). That is, the following code produces v again: (with-input-from-string (~v v) (compose read eval)) (or printing v and pasting it into the repl, as Alex said.
  • display is intended for human consumption only; that is, there is no way to programmatically get back the original data.
4 Likes

Another question:

8.3 Reading and Writing Racket Data says:

In the format string supplied to printf, ~a displays the next argument, ~s writes the next argument, and ~v prints the next argument.

Does anyone know why the letters a, s and v were chosen for the placeholders? Do you know of any mnemonics?

Here are the names used in Common Lisp:

a = aesthetic
s = standard
v = ?

http://www.lispworks.com/documentation/HyperSpec/Front/X_Mast_9.htm

1 Like

If I use pretty-print, short data structures (probably those whose output fits on one line?) aren't wrapped. For example, this wraps lines,

(define data
  '("This is an example line"
    "Here's another one"
    "And some nested data"
    '(1 2 3)
    "And some more text"))
(pretty-print data)]

'("This is an example line"
  "Here's another one"
  "And some nested data"
  '(1 2 3)
  "And some more text")

However, this doesn't wrap:

(define data
  '("foo"
    '(1 2 3)
    "bar"))
(pretty-print data)

'("foo" '(1 2 3) "bar")

Is there a way to enforce nested indented printing as in the first example, apart from the workaround of setting the pretty-print-columns parameter? I realize that a different approach may also split the inner list with the numbers over several lines, but that would be ok (at least in some cases).

Is what you want that every list of length N appears of N different lines?

Yes, that would be an option. Even if this may create "too many" lines, it may be better than the current formatting, depending on the data.

(It may be nice to have an algorithm that tries to wrap and nest lines to maximize readability even if short sub-structures are included, but I'm aware that such "readability" is quite subjective, so this problem may be difficult to solve.)

I think setting pretty-print-columns to 1 is what you want, then.

1 Like