# Paren-shapes and raco fmt

**URL:** <https://racket.discourse.group/t/paren-shapes-and-raco-fmt/3994>\
**Category:** General\
**Created:** [November 3, 2025, 6:39pm UTC](https://racket.discourse.group/t/paren-shapes-and-raco-fmt/3994 "2025-11-03T18:39:17Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![jbclements](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/jbclements/32/11_2.png) [@jbclements](https://racket.discourse.group/u/jbclements)\
**Post date:** [November 3, 2025, 6:39pm UTC](https://racket.discourse.group/t/paren-shapes-and-raco-fmt/3994/1 "2025-11-03T18:39:17Z")

</div>

I'm trying to staple together existing tools so that I can take a datum such as

`'(let ((a = 5)) (cond (b c) (d e)))`

and generate this string:

```scheme
{let {[a = 5]}
  (cond [b c]
        [d e])}

```

I think I can almost make this work by applying syntax wrappers with 'paren-shape syntax-properties, then using @sorawee 's `fmt` package. ... but I don't see a "front door" for `fmt` that accepts a syntax object, and I don't know how to write a syntax object to a string in a way that uses the paren-shape syntax properties (which would allow me to use the string-based front door). Either of these would solve the problem. Can anyone point me to either one of these?

---

<div class="post-metadata">

**Author:** ![AlexKnauth](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/alexknauth/32/242_2.png) [@AlexKnauth](https://racket.discourse.group/u/AlexKnauth)\
**Post date:** [November 3, 2025, 7:43pm UTC](https://racket.discourse.group/t/paren-shapes-and-raco-fmt/3994/2 "2025-11-03T19:43:48Z")

</div>

`syntax->string` is an alternative that does accept a syntax object, but it also doesn't do the formatting that `fmt` does: it uses the syntax source locations to determine indentation and stuff, not good-style formatting rules

---

<div class="post-metadata">

**Author:** ![jbclements](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/jbclements/32/11_2.png) [@jbclements](https://racket.discourse.group/u/jbclements)\
**Post date:** [November 3, 2025, 8:02pm UTC](https://racket.discourse.group/t/paren-shapes-and-raco-fmt/3994/3 "2025-11-03T20:02:46Z")

</div>

Perfect! So if I funnel the result of syntax-\>string into fmt, I should be all good.

---

<div class="post-metadata">

**Author:** ![sorawee](https://avatars.discourse-cdn.com/v4/letter/s/ea5d25/32.png) [@sorawee](https://racket.discourse.group/u/sorawee)\
**Post date:** [November 3, 2025, 8:27pm UTC](https://racket.discourse.group/t/paren-shapes-and-raco-fmt/3994/4 "2025-11-03T20:27:42Z")

</div>

If I recall correctly, this won't work out if the box, but it should be possible with a minor change to fmt. I can take a look this weekend if you have not already solved it by then.

---

<div class="post-metadata">

**Author:** ![jbclements](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/jbclements/32/11_2.png) [@jbclements](https://racket.discourse.group/u/jbclements)\
**Post date:** [February 12, 2026, 7:35am UTC](https://racket.discourse.group/t/paren-shapes-and-raco-fmt/3994/5 "2026-02-12T07:35:44Z")

</div>

@sorawee Hey! I'm back. I took another look at this, and basically, I want some encouragement that what I'm trying to do can be done, before looking at the part of the implementation that pretty clearly seems to be labeled "don't look in here!".

Here's a self-contained file that uses fmt; I've included what I'm hoping to get fmt to output, along with what it currently wants to output. I see that all of the `let`s went back to round brackets, for one thing. Also, I'm not sure how to get it to prefer the indentation where b and equals and the beginning of the binding for `b` all appear on the same line.

Any pointers to docs or examples appreciated!

```racket
#lang racket

;(require "handin-render.rkt")
(require fmt)

;; given an s-expression, output a string representation where parens by default
;; are rendered as curly-braces
(define (encurlify sexp)
  (match sexp
    [(list elts ...)
     (string-append "{"
                    (apply
                     string-append
                     (add-between (map encurlify elts) " "))
                    "}")]
    [other (~s sexp)]))

(define translated
  '(let ((a = 9) (b = ((fun (y) => (let ((p = 2)) in (+ 3 19))) (fun (x) => 14))))
     in
     (+ b a))

  #;(rkt-holang-render
   '(let ([a 9]
          [b ((λ (y) (let ([p 2]) (+ 3 19)))
              (λ (x) 14))])
      (+ b a))))

;; ideal:
#|
{let {{a = 9}
      {b = {{fun {y} =>
              {let {{p = 2}}
                in
                {+ 3 19}}}
            {fun {x} => 14}}}}
  in
  {+ b a}}
|#

(define curly-string
  (encurlify translated))

(display
(program-format
 #:width 70
 curly-string))

;; current output:
#|
(let ({a = 9}
      {b
       =
       {{fun
         {y}
         =>
         (let ({p = 2})
           in
           {+ 3 19})}
        {fun {x} => 14}}})
  in
  {+ b a})
|#

```

---

<div class="post-metadata">

**Author:** ![sorawee](https://avatars.discourse-cdn.com/v4/letter/s/ea5d25/32.png) [@sorawee](https://racket.discourse.group/u/sorawee)\
**Post date:** [February 13, 2026, 9:24am UTC](https://racket.discourse.group/t/paren-shapes-and-raco-fmt/3994/6 "2026-02-13T09:24:59Z")

</div>

Hi John!

Fixing `fun` formatting is just a matter of writing an appropriate formatter for it. It’s a bit more difficult to fix `{b = ...}` because `fmt` doesn’t have a mechanism to support infix operator formatting yet at all, so the core of `fmt` will require an adjustment, but it should be possible to do so.

Here’s my quick attempt to fix the formatting of `fun`.

```scheme
#lang racket

;(require "handin-render.rkt")
(require fmt
         pretty-expressive)

;; given an s-expression, output a string representation where parens by default
;; are rendered as curly-braces
(define (encurlify sexp)
  (match sexp
    [(list elts ...)
     (string-append "{"
                    (apply
                     string-append
                     (add-between (map encurlify elts) " "))
                    "}")]
    [other (~s sexp)]))

(define translated
  '(let ((a = 9) (b = ((fun (y) => (let ((p = 2)) in (+ 3 19))) (fun (x) => 14))))
     in
     (+ b a)))

;; ideal:
#|
{let {{a = 9}
      {b = {{fun {y} =>
              {let {{p = 2}}
                in
                {+ 3 19}}}
            {fun {x} => 14}}}}
  in
  {+ b a}}
>#

(define curly-string
  (encurlify translated))

(define-pretty (format-fun)
  #:type node?
  (match (extract (node-content doc) (list #t #t #f))
    [#f (format-#%app doc)]
    [(list (list -macro-name -e ...) unfits tail)
     (define first-line
       (match -e
         ['() (pretty -macro-name)]
         [_
          (define args (map pretty -e))
          (<+s> (pretty -macro-name) (alt (v-concat args) (flatten (as-concat args))))]))
     (pretty-node
      #:adjust '("{" "}")
      #:unfits unfits
      (try-indent
       #:because-of (append (cons -macro-name -e) tail)
       (alt
        ;; multi line format
        (<$> first-line
             (<+> space ((format-vertical/helper
                          #:body-formatter pretty
                          #:kw-map default-kw-map)
                         tail)))
        ;; single line format
        (<+> first-line
             space
             ((format-horizontal/helper
               #:body-formatter pretty
               #:kw-map default-kw-map)
              tail)))))]))

(display
 (program-format
  #:width 70
  curly-string
  #:formatter-map
  (λ (sym)
    (case sym
      [("fun") (format-fun)]
      [else #f]))))

;; current output:
#|
(let ({a = 9}
      {b
       =
       {{fun {y} =>
          (let ({p = 2})
            in
            {+ 3 19})}
        {fun {x} => 14}}})
  in
  {+ b a})
>#

```

One annoying thing is that the default of `#:adjust` in `pretty-node` is to convert things to parentheses. This is the reason why `let` gets converted from curly braces to parens. We could improve this by creating a global parameter and modify `pretty-node` to consult this parameter, and see if the conversion should happen or not.

---

<div class="post-metadata">

**Author:** ![jbclements](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/jbclements/32/11_2.png) [@jbclements](https://racket.discourse.group/u/jbclements)\
**Post date:** [February 14, 2026, 7:45pm UTC](https://racket.discourse.group/t/paren-shapes-and-raco-fmt/3994/7 "2026-02-14T19:45:48Z")

</div>

Fantastic! Many thanks. WRT the formatting of the binding clauses of let, the obvious way of avoiding the "infix operator" question is to punt upward, and say that this is part of the job of `let`. Is that not a reasonable approach? I can take a look at what you've given me for `fun`, and poke at it a bit, many thanks for the demo code.

It also seems to me that it might be nice to put something like this in the docs? Even if it's all unstable?

Again, many thanks!

---

<div class="post-metadata">

**Author:** ![sorawee](https://avatars.discourse-cdn.com/v4/letter/s/ea5d25/32.png) [@sorawee](https://racket.discourse.group/u/sorawee)\
**Post date:** [February 16, 2026, 8:19am UTC](https://racket.discourse.group/t/paren-shapes-and-raco-fmt/3994/8 "2026-02-16T08:19:52Z")

</div>

That is indeed a reasonable approach if `=` is only used in `let`! I somehow was under the impression that there are more places that use infix operators, but that doesn’t seem to be the case.

In fact, the current `let` formatter already handles the formatting of binding pairs as you suggested: it converts parens to square brackets, to fix the Scheme-style code. We could do something similar here.
