Skip to content

WIP: Add keyword-prefixed-field - #5544

Draft
jackfirth wants to merge 2 commits into
racket:masterfrom
jackfirth:feature-keyword-prefixed-field
Draft

jackfirth wants to merge 2 commits into
racket:masterfrom
jackfirth:feature-keyword-prefixed-field

Conversation

@jackfirth

Copy link
Copy Markdown
Contributor

Checklist

  • Feature
  • tests included
  • documentation

Description of change

When using make-constructor-style-printer, the current guidance for handling fields that are passed to the struct's constructor by name instead of by position is to use unquoted-printing-string to insert quoteless keywords into the printed representation. This has downsides when the struct is too long to print on a single line with the pretty printer. For example, printing (foo #:a 1 #:b 2) over multiple lines would print out thusly:

(foo
 #:a
 1
 #:b
 2)

This is unreadable. Far better is to keep each keyword and argument on the same line unless that specific field is the one that's long enough to force the line break. And in that instance, it would be better to indent the field value slightly to better communicate via indentation that it's associated with the keyword on the line above it. Supposing that the #:a field in the example above was the one that was excessively long, the above would be more readable like this:

(foo
 #:a
   1
 #:b 2)

This commit adds a keyword-prefixed-field struct wrapping a keyword and an arbitrary value which, when printed, implements the above behavior for a single keyword field. By combining this with make-constructor-style-printer, we can achieve the desired pretty printing behavior.

@LiberalArtist

Copy link
Copy Markdown
Contributor

I like this idea!

I wonder if there's a slightly more general version—but I wouldn't want to worsen the ergonomics for this use-case.

For example, I thought of dictionary types, which similarly suffer from their key–value relationships not being communicated to the pretty printer, but where the keys need not be keywords. A complication is that, for keyword-prefixed-field, it makes sense to ignore write/print/display mode for the key part, but that would not be true for a dictionary type.

I also wondered about a generalization to more than two components to try to keep together, but that seemed like a point of diminishing usefulness.

Maybe the answer is that the differences would be big enough that they shouldn't conflated with keyword-prefixed-field, especially since this use case has been prominent enough to document (for nearly 9 years! I hadn't noticed 5519a8a until seeing this PR).

@LiberalArtist

Copy link
Copy Markdown
Contributor

One possible answer (don’t know if it’s a good one or not) could be to keep keyword-prefixed-field as is, and separately add a (struct pretty-print-together (lst)) that prints like a list, but with no parentheses.

When using `make-constructor-style-printer`, the current guidance for handling fields that are passed to the struct's constructor by name instead of by position is to use `unquoted-printing-string` to insert quoteless keywords into the printed representation. This has downsides when the struct is too long to print on a single line with the pretty printer. For example, printing `(foo #:a 1 #:b 2)` over multiple lines would print out thusly:

```
(foo
 #:a
 1
 #:b
 2)
```

This is unreadable. Far better is to keep each keyword and argument on the same line unless *that specific field* is the one that's long enough to force the line break. And in that instance, it would be better to indent the field value slightly to better communicate via indentation that it's associated with the keyword on the line above it. Supposing that the `#:a` field in the example above was the one that was excessively long, the above would be more readable like this:

```
(foo
  #:a
    1
  #:b 2)
```

This commit adds a `keyword-prefixed-field` struct wrapping a keyword and an arbitrary value which, when printed, implements the above behavior for a single keyword field. By combining this with `make-constructor-style-printer`, we can achieve the desired pretty printing behavior.
@jackfirth
jackfirth force-pushed the feature-keyword-prefixed-field branch from 500c65b to ad38ea8 Compare August 27, 2026 02:48
@sorawee

sorawee commented Aug 27, 2026 •

Copy link
Copy Markdown
Collaborator

Should fmt to be adjusted accordingly? I think it will cause a lot of changes though. Do you want to post to Discourse first to see whether Racket users have any feedback on this "indentation" style?

@jackfirth

Copy link
Copy Markdown
Contributor Author

I don't think fmt needs to change currently. I don't actually personally think that whitespace formatting for values printed to REPLs needs to exactly match whitespace styles for the expressions that construct those values. I went with the indentation style I did because I find keyword struct REPL output far more difficult to read with less indentation.

@jackfirth

jackfirth commented Aug 27, 2026 •

Copy link
Copy Markdown
Contributor Author

@LiberalArtist Regarding generalization, I think there's some options we could explore. But I'd like to handle them separately, in future PRs. Right now all I personally need is this specific API, and I think it's useful and ergonomic enough on its own that we'd still want it as-is even if we had a more general solution.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants