String Formatting in Racket

#lang racket

(struct point (x y))

(define (main)
  ; Racket offers several printing functions for formatting values.
  ; For example, this prints an instance of our `point` struct.
  (let ([p (point 1 2)])
    (printf "struct1: ~a\n" p)

    ; To include the struct's field names, we can use `~v`.
    (printf "struct2: ~v\n" p)

    ; The `~s` format specifier prints a Racket syntax representation
    ; of the value, i.e. the source code snippet that would produce that value.
    (printf "struct3: ~s\n" p)

    ; To print the type of a value, we can use `object-name`.
    (printf "type: ~a\n" (object-name (point-x p)))

    ; Formatting booleans is straightforward.
    (printf "bool: ~a\n" #t)

    ; There are many options for formatting integers.
    ; Use `~a` for standard formatting.
    (printf "int: ~a\n" 123)

    ; This prints a binary representation.
    (printf "bin: ~b\n" 14)

    ; This prints the character corresponding to the given integer.
    (printf "char: ~a\n" (integer->char 33))

    ; `~x` provides hex encoding.
    (printf "hex: ~x\n" 456)

    ; For basic decimal formatting of floats, use `~f`.
    (printf "float1: ~f\n" 78.9)

    ; `~e` formats the float in scientific notation.
    (printf "float2: ~e\n" 123400000.0)

    ; For basic string printing, use `~a`.
    (printf "str1: ~a\n" "\"string\"")

    ; To double-quote strings, use `~s`.
    (printf "str2: ~s\n" "\"string\"")

    ; To print a representation of a pointer, we can use `eq-hash-code`.
    (printf "pointer: ~x\n" (eq-hash-code p))

    ; When formatting numbers, you can control the width and precision.
    ; To specify the width of an integer, use a number after `~`.
    (printf "width1: |~6a|~6a|\n" 12 345)

    ; For floats, you can specify both width and precision.
    (printf "width2: |~6,2f|~6,2f|\n" 1.2 3.45)

    ; To left-justify, use the `-` flag.
    (printf "width3: |~-6,2f|~-6,2f|\n" 1.2 3.45)

    ; You can also control width when formatting strings.
    (printf "width4: |~6a|~6a|\n" "foo" "b")

    ; To left-justify strings, use the `-` flag as with numbers.
    (printf "width5: |~-6a|~-6a|\n" "foo" "b")

    ; `format` returns a formatted string without printing it.
    (let ([s (format "format: a ~a" "string")])
      (displayln s))

    ; You can format+print to output ports other than
    ; the current output port using `fprintf`.
    (fprintf (current-error-port) "io: an ~a\n" "error"))
)

(main)

To run this program, save it as string-formatting.rkt and use the racket command:

$ racket string-formatting.rkt
struct1: #<point>
struct2: #(struct:point 1 2)
struct3: (point 1 2)
type: integer
bool: #t
int: 123
bin: 1110
char: !
hex: 1c8
float1: 78.9
float2: 1.234e+8
str1: "string"
str2: "\"string\""
pointer: 1234567 ; This will be a different number each time
width1: |    12|   345|
width2: |  1.20|  3.45|
width3: |1.20  |3.45  |
width4: |   foo|     b|
width5: |foo   |b     |
format: a string
io: an error

This Racket program demonstrates various string formatting techniques, similar to the original Go example. It uses Racket’s printf function, which is similar to Go’s fmt.Printf. The format specifiers are slightly different:

  • ~a is used for general formatting (similar to %v in Go)
  • ~s is used for Racket syntax representation (similar to %#v in Go)
  • ~v is used for detailed structure printing (similar to %+v in Go)
  • ~f is used for float formatting
  • ~e is used for scientific notation
  • ~x is used for hexadecimal formatting
  • ~b is used for binary formatting

Racket doesn’t have a built-in way to print type information like Go’s %T, so we use object-name on a struct field instead.

The program also demonstrates width and precision control, left-justification, and using different output ports (like current-error-port, which is similar to os.Stderr in Go).

Note that Racket doesn’t have a direct equivalent to Go’s pointers, so we use eq-hash-code to get a unique identifier for an object, which serves a similar purpose in this context.