Beautiful Racket
1 Installation
2 Conditionals
while
until
3 Datums
format-datum
format-datums
datum?
4 Debugging
report
report*
report-datum
5 Define
define-cases
6 Macro
define-macro
define-macro-cases
caller-stx
define-unhygienic-macro
7 Syntax
with-pattern
pattern-case
pattern-case-filter
prefix-id
suffix-id
infix-id
strip-bindings
replace-bindings
stx-flatten
8 Indentation
char
line
previous-line
next-line
line-chars
line-start
line-end
line-start-visible
line-end-visible
line-first-visible-char
line-last-visible-char
line-indent
apply-indenter
string-indents
9 Lists
values->list
push!
pop!
10 Reader utilities
apply-reader
11 The br teaching languages
8.12

Beautiful Racket🔗ℹ

Matthew Butterick <mb@mbtype.com>

Beautiful Racket is a book about making programming languages with Racket.

This library provides the #lang br teaching language used in the book, as well as supporting modules that can be used in other programs.

This library is designed to smooth over some of the small idiosyncrasies and inconsistencies in Racket, so that those new to Racket are more likely to say “ah, that makes sense” rather than “huh? what?”

1 Installation🔗ℹ

If you want all the code & documentation, install the package beautiful-racket.

If you just want the code modules (for instance, for use as a dependency in another project) install the package beautiful-racket-lib.

If you just want the br/macro and br/syntax modules, install the package beautiful-racket-macro.

2 Conditionals🔗ℹ

 (require br/cond) package: beautiful-racket-lib

syntax

(while cond body ...)

Loop over body as long as cond is not #f. If cond starts out #f, body is never evaluated.

Examples:
> (let ([x 42])
    (while (positive? x)
           (set! x (- x 1)))
    x)

0

> (let ([x 42])
    (while (negative? x)
           (unleash-zombie-army))
    x)

42

syntax

(until cond body ...)

Loop over body until cond is not #f. If cond starts out not #f, body is never evaluated.

Examples:
> (let ([x 42])
    (until (zero? x)
           (set! x (- x 1)))
    x)

0

> (let ([x 42])
    (until (= 42 x)
           (destroy-galaxy))
    x)

42

3 Datums🔗ℹ

 (require br/datum) package: beautiful-racket-lib

A datum is a literal representation of a single unit of Racket code, also known as an S-expression. Unlike a string, a datum preserves the internal structure of the S-expression. Meaning, if the S-expression is a single value, or list-shaped, or tree-shaped, so is its corresponding datum.

Datums are made with quote or its equivalent notation, the ' prefix (see Quoting: quote and ).

When I use “datum” in its specific Racket sense, I use “datums” as its plural rather than “data” because that term has an existing, more generic meaning.

procedure

(format-datum datum-form val ...)  (or/c datum? void?)

  datum-form : datum?
  val : any/c?
Similar to format, but the template datum-form is a datum, rather than a string, and the function returns a datum, rather than a string. Otherwise, the same formatting escapes can be used in the template (see fprintf).

Two special cases. First, a string that describes a list of datums is parenthesized so the result is a single datum. Second, an empty string returns void (not #f, because that’s a legitimate datum).

Examples:
> (format-datum '42)

format-datums: contract violation

  expected: datum?

  given: 42

> (format-datum '~a "foo")

'foo

> (format-datum '(~a ~a) "foo" 42)

'(foo 42)

> (format-datum '~a "foo bar zam")

'(foo bar zam)

> (void? (format-datum '~a ""))

#t

> (format-datum '~a #f)

#f

procedure

(format-datums datum-form vals ...)

  (listof (or/c list? symbol?))
  datum-form : (or/c list? symbol?)
  vals : (listof any/c?)
Like format-datum, but applies datum-form to the lists of vals in similar way to map, where values for the format string are taken from the lists of vals in parallel. This means that a) datum-form must accept as many arguments as there are lists of vals, and b) the lists of vals must all have the same number of items.

Examples:
> (format-datums '~a '("foo" "bar" "zam"))

'(foo bar zam)

> (format-datums '(~a 42) '("foo" "bar" "zam"))

'((foo 42) (bar 42) (zam 42))

> (format-datums '(~a ~a) '("foo" "bar" "zam") '(42 43 44))

'((foo 42) (bar 43) (zam 44))

> (format-datums '42 '("foo" "bar" "zam"))

format-datums: contract violation

  expected: datum?

  given: 42

> (format-datums '(~a ~a) '("foo" "bar" "zam") '(42))

map: all lists must have same size

  first list length: 3

  other list length: 1

  procedure: #<procedure:...et-lib/br/datum.rkt:30:13>

procedure

(datum? x)  boolean?

  x : any/c
Return #t if x is a list? or a symbol?.

Examples:
> (datum? '(a b c d e f))

#t

> (datum? 'a)

#t

> (datum? "a")

#f

4 Debugging🔗ℹ

 (require br/debug) package: beautiful-racket-lib

syntax

(report expr)

(report expr maybe-name)
Print the name and value of expr to current-error-port, but also return the evaluated result of expr as usual. This lets you see the value of an expression or variable at runtime without disrupting any of the surrounding code. Optionally, you can use maybe-name to change the name shown in current-error-port.

For instance, suppose you wanted to see how first-condition? was being evaluted in this expression:

(if (and (first-condition? x) (second-condition? x))
  (one-thing)
  (other-thing))

You can wrap it in report and find out:

(if (and (report (first-condition? x)) (second-condition? x))
  (one-thing)
  (other-thing))

This code will run the same way as before. But when it reaches first-condition?, you willl see in current-error-port:

(first-condition? x) = #t

You can also add standalone calls to report as a debugging aid at points where the return value will be irrelevant, for instance:

(report x x-before-function)
(if (and (report (first-condition? x)) (second-condition? x))
  (one-thing)
  (other-thing))

x-before-function = 42
(first-condition? x) = #t

But be careful — in the example below, the result of the if expression will be skipped in favor of the last expression, which will be the value of x:

(if (and (report (first-condition? x)) (second-condition? x))
  (one-thing)
  (other-thing))
(report x)

syntax

(report* expr ...)

Apply report separately to each expr in the list.

syntax

(report-datum stx-expr)

(report-datum stx-expr maybe-name)
A variant of report for use with Syntax Objects. Rather than print the whole object (as report would), report-datum prints only the datum inside the syntax object, but the return value is the whole syntax object.

5 Define🔗ℹ

 (require br/define) package: beautiful-racket-lib

This module also exports the bindings from br/macro.

syntax

(define-cases id
  [pat body ...+] ...+)
Define a function that behaves differently depending on how many arguments are supplied (also known as arity). Like cond, you can have any number of branches. Each branch starts with a pat that accepts a certain number of arguments. If the current invocation of the function matches the number of arguments in pat, then the body on the right-hand side is evaluated. If there is no matching case, an arity error arises. (Derived from case-lambda, whose notation you might prefer.)

Examples:
> (define-cases f
    [(f arg1) (* arg1 arg1)]
    [(f arg1 arg2) (* arg1 arg2)]
    [(f arg1 arg2 arg3 arg4) (* arg1 arg2 arg3 arg4)])
> (f 4)

16

> (f 6 7)

42

> (f 1 2 3 4)

24

> (f "three" "arguments" "will-trigger-an-error")

f: arity mismatch;

 the expected number of arguments does not match the given

number

  given: 3

  arguments...:

   "three"

   "arguments"

   "will-trigger-an-error"

> (define-cases f2
    [(f2) "got zero args"]
    [(f2 . args) (format "got ~a args" (length args))])
> (f2)

"got zero args"

> (f2 6 7)

"got 2 args"

> (f2 1 2 3 4)

"got 4 args"

> (f2 "three" "arguments" "will-not-trigger-an-error-this-time")

"got 3 args"

6 Macro🔗ℹ

 (require br/macro)
  packages: beautiful-racket-lib, beautiful-racket-macro

syntax

(define-macro (id pat-arg ...) result-expr ...+)

(define-macro id #'other-id)
(define-macro id (lambda (arg-id) result-expr ...+))
(define-macro id transformer-id)
(define-macro id syntax-object)
Create a macro using one of the subforms above, which are explained below:

(define-macro (id pat-arg ...) result-expr ...+)

If the first argument is a syntax pattern starting with id, then create a syntax transformer for this pattern using result-expr ... as the return value. As usual, result-expr ... needs to return a syntax object or you’ll get an error.

The syntax-pattern notation is the same as syntax-case, with one key difference. If a pat-arg has a name written in CAPS, it’s treated as a named wildcard (meaning, it will match any expression in that position, and can be subsequently referred to by that name). Otherwise, pat-arg is treated as a literal (meaning, it will only match the same expression). If pat-arg is a literal identifier, it will only match another identifier with the same name and the same binding (in other words, identifiers are tested with free-identifier=?).

For instance, the sandwich macro below requires three arguments, and the third must be please, but the other two are wildcards:

Examples:
> (define-macro (sandwich TOPPING FILLING please)
    #'(format "I love ~a with ~a." 'FILLING 'TOPPING))
> (sandwich brie ham)

sandwich: no matching case for calling pattern

  in: (sandwich brie ham)

> (sandwich brie ham now)

sandwich: no matching case for calling pattern

  in: (sandwich brie ham now)

> (sandwich brie ham please)

"I love ham with brie."

> (sandwich banana bacon please)

"I love bacon with banana."

The ellipsis ... can be used with a wildcard to match a list of arguments. Please note: though a wildcard standing alone must match one argument, once you add an ellipsis, it’s allowed to match zero:

Examples:
> (define-macro (pizza TOPPING ...)
    #'(string-join (cons "Waiter!"
                         (list (format "More ~a!" 'TOPPING) ...))
                   " "))
> (pizza mushroom)

"Waiter! More mushroom!"

> (pizza mushroom pepperoni)

"Waiter! More mushroom! More pepperoni!"

> (pizza)

"Waiter!"

The capitalization requirement for a wildcard pat-arg makes it easy to mix literals and wildcards in one pattern. But it also makes it easy to mistype a pattern and not get the wildcard you were expecting. Below, bad-squarer doesn’t work because any-number is meant to be a wildcard. But it’s not in caps, so it’s considered a literal, and it triggers an error:

Examples:
> (define-macro (bad-squarer any-number)
    #'(* any-number any-number))
> (bad-squarer 0+10i)

bad-squarer: no matching case for calling pattern

  in: (bad-squarer 0+10i)

The error is cleared when the argument is in caps, thus making it a wildcard:

Examples:
> (define-macro (good-squarer ANY-NUMBER)
    #'(* ANY-NUMBER ANY-NUMBER))
> (good-squarer 0+10i)

-100

You can use the special variable caller-stx — available only within the body of define-macro — to access the original input argument to the macro.

Examples:
> (define-macro (inspect ARG ...)
    (with-pattern ([CALLER-STX (syntax->datum caller-stx)])
      #`(displayln
         (let ([calling-pattern 'CALLER-STX])
           (format "Called as ~a with ~a args"
                   calling-pattern
                   (length (cdr calling-pattern)))))))
> (inspect)

Called as (inspect) with 0 args

> (inspect "foo" "bar")

Called as (inspect foo bar) with 2 args

> (inspect #t #f #f #t)

Called as (inspect #t #f #f #t) with 4 args

This subform of define-macro is useful for macros that have one calling pattern. To make a macro with multiple calling patterns, see define-macro-cases. }

(define-macro id #'other-id)

If the first argument is an identifier id and the second a syntaxed identifier that looks like #'other-id, create a rename transformer, which is a fancy term for “macro that replaces id with other-id.” (This subform is equivalent to make-rename-transformer.)

Why do we need rename transformers? Because an ordinary macro operates on its whole calling expression (which it receives as input) like (macro-name this-arg that-arg . and-so-on). By contrast, a rename transformer operates only on the identifier itself (regardless of where that identifier appears in the code). It’s like making one identifier into an alias for another identifier.

Below, notice how the rename transformer, operating in the macro realm, approximates the behavior of a run-time assignment.

Examples:
> (define foo 'foo-value)
> (define bar foo)
> bar

'foo-value

> (define-macro zam-macro #'foo)
> zam-macro

'foo-value

> (define add +)
> (add 20 22)

42

> (define-macro sum-macro #'+)
> (sum-macro 20 22)

42

(define-macro id (lambda (arg-id) result-expr ...+))

If the first argument is an id and the second a single-argument function, create a macro called id that uses the function as a syntax transformer. This function must return a syntax object, otherwise you’ll trigger an error. Beyond that, the function can do whatever you like. (This subform is equivalent to define-syntax.)

Examples:
> (define-macro nice-sum (lambda (stx) #'(+ 2 2)))
> nice-sum

4

> (define-macro not-nice (lambda (stx) '(+ 2 2)))
> not-nice

not-nice: received value from syntax expander was not syntax

  received: '(+ 2 2)

(define-macro id transformer-id)

Similar to the previous subform, but transformer-id holds an existing transformer function. Note that transformer-id needs to be visible during compile time (aka phase 1), so use define-for-syntax or equivalent.

Examples:
> (define-for-syntax summer-compile-time (lambda (stx) #'(+ 2 2)))
> (define-macro nice-summer summer-compile-time)
> nice-summer

4

> (define summer-run-time (lambda (stx) #'(+ 2 2)))
> (define-macro not-nice-summer summer-run-time)

summer-run-time: undefined;

 cannot reference an identifier before its definition

  in module: top-level

(define-macro id syntax-object)
 
  syntax-object : syntax?

If the first argument is an id and the second a syntax-object, create a syntax transformer that returns syntax-object. This is just alternate notation for the previous subform, wrapping syntax-object inside a function body. The effect is to create a macro from id that always returns syntax-object, regardless of how it’s invoked. Not especially useful within programs. Mostly handy for making quick macros at the REPL.

Examples:
> (define-macro bad-listener #'"what?")
> bad-listener

"what?"

> (bad-listener)

"what?"

> (bad-listener "hello")

"what?"

> (bad-listener 1 2 3 4)

"what?"

syntax

(define-macro-cases id
  [pattern result-expr ...+] ...+)
Create a macro called id with multiple branches, each with a pattern on the left and result-expr on the right. The input to the macro is tested against each pattern. If it matches, then result-expr is evaluated.

As with define-macro, wildcards in each syntax pattern must be in CAPS. Everything else is treated as a literal match, except for the ellipsis ... and the wildcard _.

Examples:
> (define-macro-cases yogurt
    [(yogurt) #'(displayln (format "No toppings? Really?"))]
    [(yogurt TOPPING)
     #'(displayln (format "Sure, you can have ~a." 'TOPPING))]
    [(yogurt TOPPING ANOTHER-TOPPING ... please)
     #'(displayln (format "Since you asked nicely, you can have ~a toppings."
     (length '(TOPPING ANOTHER-TOPPING ...))))]
    [(yogurt TOPPING ANOTHER-TOPPING ...)
     #'(displayln (format "Whoa! Rude people only get one topping."))])
> (yogurt)

No toppings? Really?

> (yogurt granola)

Sure, you can have granola.

> (yogurt coconut almonds hot-fudge brownie-bites please)

Since you asked nicely, you can have 4 toppings.

> (yogurt coconut almonds)

Whoa! Rude people only get one topping.

A special variable only available inside the body of define-macro or define-macro-cases. It contains the whole original syntax object that was passed to the macro.

syntax

(define-unhygienic-macro (id pat-arg ...)
  result-expr ...+)
Like define-macro, but moves result-expr into the lexical context of the calling site. For demonstration purposes only. If you really need to write an unhygienic macro, this is a rather blunt instrument.

7 Syntax🔗ℹ

 (require br/syntax)
  packages: beautiful-racket-lib, beautiful-racket-macro

syntax

(with-pattern ([pattern stx-expr] ...) body ...+)

Bind pattern variables within each pattern by matching the pattern to its respective stx-expr. These pattern variables can be used in later pattern–expression clauses, or in body. Uses the same pattern conventions as define-macro (i.e., wildcard variables must be in CAPS; everything else is treated as a literal).

Examples:
> (define-macro (m ARG)
    (with-pattern ([(1ST 2ND 3RD) #'ARG]
                   [(LEFT RIGHT) #'2ND])
      #'LEFT))
> (m ((1 2) (3 4) (5 6)))

3

syntax

(pattern-case stx ([pattern result-expr ...+] ...))

Like case, but for syntax patterns. Attempt to match stx to each successive syntax pattern. If a match is found, evaluate the result-expr on the right. Uses the same pattern conventions as define-macro (i.e., wildcard variables must be in CAPS; everything else is treated as a literal). If no match is found a syntax error is raised.

Examples:
> (define (pc stx)
    (pattern-case stx
      [(1ST 2ND 3RD) #'2ND]
      [(LEFT RIGHT) #'LEFT]))
> (pc #'(a b c))

#<syntax:eval:78:0 b>

> (pc #'(x y))

#<syntax:eval:79:0 x>

> (pc #'(f))

pattern-case: unable to match pattern for '(f)

syntax

(pattern-case-filter stxs ([pattern result-expr ...+] ...))

Attempt to match each element of stxs (which is either a list of syntax objects, or a syntaxed list) to each successive syntax pattern. Uses the same pattern conventions as define-macro (i.e., wildcard variables must be in CAPS; everything else is treated as a literal). If a match is found, evaluate the result-expr on the right. If result-expr is #f, or no match is found, then the element is skipped. The result is a list of syntax objects.

Example:
> (pattern-case-filter #'((a b c) (x y) (f))
    [(1ST 2ND 3RD) #'2ND]
    [(LEFT RIGHT) #'LEFT])

'(#<syntax:eval:81:0 b> #<syntax:eval:81:0 x>)

procedure

(prefix-id prefix 
  ... 
  id-or-ids 
  [#:source loc-stx 
  #:context ctxt-stx]) 
  (or/c identifier? (listof identifier?))
  prefix : (or string? symbol?)
  id-or-ids : (or/c identifier? (listof identifier?))
  loc-stx : syntax? = #f
  ctxt-stx : syntax? = #f
Create a new identifier within the lexical context of id-or-ids with the same name, but prefixed with prefix. If there’s more than one prefix argument, they are concatenated. If id-or-ids is a single identifier, then the function returns a single identifier. Likewise, if it’s a list of identifiers, the function returns a list of identifiers, all prefixed.

The optional loc-stx argument supplies the source location for the resulting identifier (or identifiers).

The optional ctxt-stx argument supplies the lexical context for the resulting identifier (or identifiers).

Examples:
> (define-macro ($-define ID VAL)
    (with-pattern ([PREFIXED-ID (prefix-id '$ #'ID)])
      #'(define PREFIXED-ID VAL)))
> ($-define foo 42)
> $foo

42

procedure

(suffix-id id-or-ids 
  suffix ... 
  [#:source loc-stx 
  #:context ctxt-stx]) 
  (or/c identifier? (listof identifier?))
  id-or-ids : (or/c identifier? (listof identifier?))
  suffix : (or string? symbol?)
  loc-stx : syntax? = #f
  ctxt-stx : syntax? = #f
Create a new identifier within the lexical context of id-or-ids with the same name, but suffixed with suffix. If there’s more than one suffix argument, they are concatenated. If id-or-ids is a single identifier, then the function returns a single identifier. Likewise, if it’s a list of identifiers, the function returns a list of identifiers, all suffixed.

The optional loc-stx argument supplies the source location for the resulting identifier (or identifiers).

The optional ctxt-stx argument supplies the lexical context for the resulting identifier (or identifiers).

Examples:
> (define-macro (define-% ID VAL)
    (with-pattern ([ID-SUFFIXED (suffix-id #'ID '%)])
      #'(define ID-SUFFIXED VAL)))
> (define-% foo 42)
> foo%

42

procedure

(infix-id prefix 
  id-or-ids 
  suffix ... 
  [#:source loc-stx 
  #:context ctxt-stx]) 
  (or/c identifier? (listof identifier?))
  prefix : (or string? symbol?)
  id-or-ids : (or/c identifier? (listof identifier?))
  suffix : (or string? symbol?)
  loc-stx : syntax? = #f
  ctxt-stx : syntax? = #f
Create a new identifier within the lexical context of id-or-ids with the same name, but prefixed with prefix and suffixed with suffix. If there’s more than one suffix argument, they are concatenated. If id-or-ids is a single identifier, then the function returns a single identifier. Likewise, if it’s a list of identifiers, the function returns a list of identifiers, all suffixed.

The optional loc-stx argument supplies the source location for the resulting identifier (or identifiers).

The optional ctxt-stx argument supplies the lexical context for the resulting identifier (or identifiers).

Examples:
> (define-macro ($-define-% ID VAL)
    (with-pattern ([ID-INFIXED (infix-id '$ #'ID '%)])
      #'(define ID-INFIXED VAL)))
> ($-define-% foo 42)
> $foo%

42

procedure

(strip-bindings stx)  syntax?

  stx : syntax?
Removes all bindings from stx, but preserves source-location information and properties. An alias for strip-context.

procedure

(replace-bindings stx-source stx-target)  syntax?

  stx-source : (or/c syntax? #f)
  stx-target : syntax?
Uses the bindings from stx-source to replace the bindings of all parts of stx-target, while preserving source-location information and properties. An alias for replace-context.

procedure

(stx-flatten stx)  (listof syntax?)

  stx : syntax?
Converts a hierarchical syntax object into a flat list of component syntax objects, discarding all the intervening structure. Useful when you need to get at the “bottommost” syntax objects.

Examples:
> (define my-stx #'(let ([x 42]
                      [y 25])
                  (define (f z) (* x y z))
                  (displayln (f 11))))
> (map syntax->datum (stx-flatten my-stx))

'(let x 42 y 25 define f z * x y z displayln f 11)

8 Indentation🔗ℹ

 (require br/indent) package: beautiful-racket-lib

Helper functions for DrRacket language indenters.

procedure

(char textbox position)  (or/c char? #f)

  textbox : (is-a?/c text%)
  position : (or/c exact-nonnegative-integer? #f)
Get the character in textbox that lives at position.

procedure

(line textbox position)  exact-nonnegative-integer?

  textbox : (is-a?/c text%)
  position : (or/c exact-nonnegative-integer? #f)
Get the line index in textbox that contains position.

procedure

(previous-line textbox position)

  (or/c exact-nonnegative-integer? #f)
  textbox : (is-a?/c text%)
  position : (or/c exact-nonnegative-integer? #f)
Get the line index in textbox of the line before the one that contains position.

procedure

(next-line textbox position)

  (or/c exact-nonnegative-integer? #f)
  textbox : (is-a?/c text%)
  position : (or/c exact-nonnegative-integer? #f)
Get the line index in textbox of the line after the one that contains position.

procedure

(line-chars textbox line-idx)  (or/c (listof char?) #f)

  textbox : (is-a?/c text%)
  line-idx : (or/c exact-nonnegative-integer? #f)
Get the chars in textbox on line line-idx.

procedure

(line-start textbox line-idx)

  (or/c exact-nonnegative-integer? #f)
  textbox : (is-a?/c text%)
  line-idx : (or/c exact-nonnegative-integer? #f)
Get the starting character position in textbox of line line-idx (or #f if there is no such line). To get the actual character, pass the return value to char.

procedure

(line-end textbox line-idx)

  (or/c exact-nonnegative-integer? #f)
  textbox : (is-a?/c text%)
  line-idx : (or/c exact-nonnegative-integer? #f)
Get the ending character position in textbox of line line-idx (or #f if there is no such line). To get the actual character, pass the return value to char.

procedure

(line-start-visible textbox line-idx)

  (or/c exact-nonnegative-integer? #f)
  textbox : (is-a?/c text%)
  line-idx : (or/c exact-nonnegative-integer? #f)

procedure

(line-end-visible textbox line-idx)

  (or/c exact-nonnegative-integer? #f)
  textbox : (is-a?/c text%)
  line-idx : (or/c exact-nonnegative-integer? #f)
Like line-start and line-end, but skips whitespace characters. To get the actual character, pass the return value to char, or use line-first-visible-char and line-last-visible-char.

procedure

(line-first-visible-char textbox line-idx)  (or/c char? #f)

  textbox : (is-a?/c text%)
  line-idx : (or/c exact-nonnegative-integer? #f)
Convenient notation for (char textbox (line-start-visible textbox line-idx)).

procedure

(line-last-visible-char textbox line-idx)  (or/c char? #f)

  textbox : (is-a?/c text%)
  line-idx : (or/c exact-nonnegative-integer? #f)
Convenient notation for (char textbox (line-end-visible textbox line-idx)).

procedure

(line-indent textbox line-idx)

  (or/c exact-nonnegative-integer? #f)
  textbox : (is-a?/c text%)
  line-idx : (or/c exact-nonnegative-integer? #f)
Get the length of the indent of line line-idx in textbox.

procedure

(apply-indenter indenter-proc    
  textbox-or-str)  string?
  indenter-proc : procedure?
  textbox-or-str : (or/c (is-a?/c text%) string?)
Apply indenter-proc to the text in textbox-or-str and return an indented string. Useful for unit testing.

procedure

(string-indents str)  (listof exact-nonnegative-integer?)

  str : string?
Lists the indents at the beginning of each line in str. Useful for unit testing.

9 Lists🔗ℹ

 (require br/list) package: beautiful-racket-lib

syntax

(values->list values)

Convert values to a simple list.

Examples:
> (split-at '(a b c d e f) 3)

'(a b c)

'(d e f)

> (values->list (split-at '(a b c d e f) 3))

'((a b c) (d e f))

syntax

(push! list-id val)

Mutate list-id by putting val on top.

Examples:
> (define xs '(2 3 4))
> (push! xs 1)
> xs

'(1 2 3 4)

syntax

(pop! list-id)

Mutate list-id by removing the topmost element, and return this element as the result.

Examples:
> (define xs '(1 2 3 4))
> (define top (pop! xs))
> top

1

> xs

'(2 3 4)

10 Reader utilities🔗ℹ

 (require br/reader-utils) package: beautiful-racket-lib

procedure

(apply-reader read-syntax-proc source-str)  datum?

  read-syntax-proc : procedure?
  source-str : string?
Applies read-syntax-proc to source-str as if it were being read in from a source file. Perhaps helpful for testing & debugging.

11 The br teaching languages🔗ℹ

The br language is designed to be used for the samples and tutorials in Beautiful Racket. The language is a convenience variant of racket/base that includes the modules listed above, plus a number of other Racket libraries. The specifics are not hugely relevant. Why not? Because in the future, br will grow depending on the needs of the Beautiful Racket book. Therefore, it’s probably not what you want to use for your own projects. See the appendix of Beautiful Racket for more information.
br/quicklang is a variant of the br language that automatically exports #%top, #%app, #%datum, and #%top-interaction. As above, if you’re making your own language, you should build on racket/base, because br/quicklang does nothing that you couldn’t do yourself in two lines of code.