diff --git a/beautiful-racket-lib/br/scribblings/br.scrbl b/beautiful-racket-lib/br/scribblings/br.scrbl index c575cec..3d8501c 100644 --- a/beautiful-racket-lib/br/scribblings/br.scrbl +++ b/beautiful-racket-lib/br/scribblings/br.scrbl @@ -1,51 +1,163 @@ #lang scribble/manual -@(require (for-label br/conditional)) +@(require (for-label racket/base racket/contract br/cond br/datum)) + +@(require scribble/eval) + +@(define my-eval (make-base-eval)) +@(my-eval `(require br/cond br/datum)) + @title[#:style 'toc]{Beautiful Racket} @author[(author+email "Matthew Butterick" "mb@mbtype.com")] -Beautiful Racket @link["http://beautifulracket.com"]{is a book} about making programming languages with Racket. +@defmodule[br] + +@link["http://beautifulracket.com"]{@italic{Beautiful Racket}} is a book about making programming languages with Racket. This library provides the @tt{#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?'' -@;defmodulelang[br] -@defmodule[br] +@section{The @tt{br} language(s)} + +@defmodulelang[br] + + +@defmodulelang[br/quicklang] -@tt{#lang br} is a teaching language designed to smooth over some of the small idiosyncrasies and inconsistencies in Racket, so that those new to Racket will say ``ah, that makes sense'' rather than ``huh? what?'' @tt{#lang br} is not meant to hide the true nature of Racket, but rather defer certain parts of the learning curve. -To that end, this documentation not only explains the functions and forms in the Beautiful Racket library, but also how they depart from traditional or idiomatic Racket. (BTW ``Beautiful Racket'' is the name of the book, not an implication that the rest of Racket is less than beautiful. It is! But one thing at a time.) @section{Conditionals} -@defmodule[br/conditional] +@defmodule[br/cond] @defform[(while cond body ...)] -Loop over @racket[_body] expressions as long as @racket[_cond] is not @racket[#f]. If @racket[_cond] starts out @racket[#f], @racket[_body] expressions are not evaluated. +Loop over @racket[_body] as long as @racket[_cond] is not @racket[#f]. If @racket[_cond] starts out @racket[#f], @racket[_body] is never evaluated. + +@examples[#:eval my-eval +(let ([x 42]) + (while (positive? x) + (set! x (- x 1))) + x) +(let ([x 42]) + (while (negative? x) + (unleash-zombie-army)) + x) +] -@defform[(until cond body ...)] -Loop over @racket[_body] expressions until @racket[_cond] is not @racket[#f]. If @racket[_cond] starts out @racket[#f], @racket[_body] expressions are not evaluated. +@defform[(until cond body ...)] +Loop over @racket[_body] until @racket[_cond] is not @racket[#f]. If @racket[_cond] starts out not @racket[#f], @racket[_body] is never evaluated. + +@examples[#:eval my-eval +(let ([x 42]) + (until (zero? x) + (set! x (- x 1))) + x) +(let ([x 42]) + (until (= 42 x) + (destroy-galaxy)) + x) +] @section{Datums} @defmodule[br/datum] +A @defterm{datum} is a literal representation of Racket code that describes 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 @racket[quote] or its equivalent notation, the @litchar{'} prefix (see @secref["quote" #:doc '(lib "scribblings/guide/guide.scrbl")]). + +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. + @defproc[ (format-datum -[datum-template symbol?] -[arg any/c?] ...) +[datum-form datum?] +[val any/c?] ...) datum?] -tk +Similar to @racket[format], but the template @racket[_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 @racket[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 @racket[void] (not @racket[#f], because that's a legitimate datum). +@examples[#:eval my-eval +(format-datum '42) +(format-datum '~a "foo") +(format-datum '(~a ~a) "foo" 42) +(format-datum '~a "foo bar zam") +(void? (format-datum '~a "")) +(format-datum '~a #f) +] + +@defproc[ +(format-datums +[datum-form datum?] +[vals (listof any/c?)] ...) +(listof datum?)] +Like @racket[format-datum], but applies @racket[_datum-form] to the lists of @racket[_vals] in similar way to @racket[map], where values for the format string are taken from the lists of @racket[_vals] in parallel. This means that a) @racket[_datum-form] must accept as many arguments as there are lists of @racket[_vals], and b) the lists of @racket[_vals] must all have the same number of items. + +@examples[#:eval my-eval +(format-datums '~a '("foo" "bar" "zam")) +(format-datums '(~a 42) '("foo" "bar" "zam")) +(format-datums '(~a ~a) '("foo" "bar" "zam") '(42 43 44)) +(format-datums '42 '("foo" "bar" "zam")) +(format-datums '(~a ~a) '("foo" "bar" "zam") '(42)) +] @section{Debugging} @defmodule[br/debug] -TK + +@defform*[((report expr) (report expr maybe-name))] +Print the name and value of @racket[_expr] to @racket[current-error-port], but also return the evaluated result of @racket[_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 @racket[_maybe-name] to change the name shown in @racket[current-error-port]. + +For instance, suppose you wanted to see how @racket[first-condition?] was being evaluted in this expression: + +@racketblock[ +(if (and (first-condition? x) (second-condition? x)) + (one-thing) + (other-thing))] + +You can wrap it in @racket[report] and find out: + +@racketblock[ +(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 @racket[first-condition?], you willl see in @racket[current-error-port]: + +@racketerror{(first-condition? x) = #t} + +You can also add standalone calls to @racket[report] as a debugging aid at points where the return value will be irrelevant, for instance: + +@racketblock[ +(report x x-before-function) +(if (and (report (first-condition? x)) (second-condition? x)) + (one-thing) + (other-thing))] + +@racketerror{x-before-function = 42 +@(linebreak)(first-condition? x) = #t} + +But be careful — in the example below, the result of the @racket[if] expression will be skipped in favor of the last expression, which will be the value of @racket[_x]: + +@racketblock[ +(if (and (report (first-condition? x)) (second-condition? x)) + (one-thing) + (other-thing)) + (report x)] + + +@defform[(report* expr ...)] +Apply @racket[report] separately to each @racket[_expr] in the list. + + +@defform*[((report-datum stx-expr) (report-datum stx-expr maybe-name))] +A variant of @racket[report] for use with @secref["stx-obj" #:doc '(lib "scribblings/guide/guide.scrbl")]. Rather than print the whole object (as @racket[report] would), @racket[report-datum] prints only the datum inside the syntax object, but the return value is the whole syntax object. + @section{Define}