1 Example Racket Languages
Every Racket program starts with #lang and the name of a language. The language determines the way the rest of the file is interpreted.
1.1 Racket Variants
Here’s an example Racket program that prints 832040 when run:
#lang racket (define (fib n) (cond [(= n 0) 0] [(= n 1) 1] [else (+ (fib (- n 1)) (fib (- n 2)))])) (fib 30)
Here’s essentially the same program, where the only difference is that it starts with #lang racket/base instead of #lang racket. The racket/base language starts with fewer functions and syntactic forms than racket, but it has everything that fib needs:
#lang racket/base (define (fib n) (cond [(= n 0) 0] [(= n 1) 1] [else (+ (fib (- n 1)) (fib (- n 2)))])) (fib 30)
As you may expect, the racket language is defined by starting with the racket/base language, and racket just adds even more syntactic forms and predefined functions. A language can start with racket/base and subtract bindings and capabilities just as easily as it can add them.
For example, the following variant of the fib example uses typed/racket/base and adds a type declaration for fib. The program runs the same and produces the same result, but if you change 30 to "30", then instead of a run-time error from =, the program won’t even run due to a static type error. In that sense, typed/racket/base has removed some capabilities from racket/base (as well as adding the additional syntax for : and the additional capability of guaranteeing something about the program’s run-time behavior):
#lang typed/racket/base (: fib (Number -> Number)) (define (fib n) (cond [(= n 0) 0] [(= n 1) 1] [else (+ (fib (- n 1)) (fib (- n 2)))])) (fib 30)
Here’s a variant of fib that uses the
racket/class library’s class and new
to implement fib in a gratuitously class-oriented way. It
uses require to add the syntactic forms of
racket/class to racket/base—
#lang racket/base (require racket/class) (define fibber% (class object% (init-field n) (define/public (get-value) (cond [(= n 0) 0] [(= n 1) 1] [else (+ (send (new fibber% [n (- n 1)]) get-value) (send (new fibber% [n (- n 2)]) get-value))])) (super-new))) (send (new fibber% [n 30]) get-value)
More usefully, here’s a variant that uses define/memo from memoize to implement a linear-time variant of fib:
#lang racket/base (require memoize) (define/memo (fib n) (cond [(= n 0) 0] [(= n 1) 1] [else (+ (fib (- n 1)) (fib (- n 2)))])) (fib 30)
1.2 Changing the Surface Syntax
The preceding examples illustrate how Racket libraries can provide the same kinds of things as languages, including both syntactic forms and functions. A language has one extra capability, however, which is to define character-level syntax, as illustrated by this Racket program:
#lang honu function fib(n) { if (n == 0) 0 else if (n == 1) 1 else fib(n-1) + fib(n-2) } fib(30)
Ultimately, honu builds on racket/base, but honu takes more direct control over the way that programs are parsed. It’s easy to see how a honu program can be translated to a racket/base program, that is indeed how honu works.
1.3 Changing the Evaluation Model
The following example does not compile to racket/base in such an obvious way, because the evaluation of a Datalog program is completely different than the evaluation of a Racket program. Still, the + and - operations of racket/base are used as “external queries” in this example:
#lang datalog (racket/base). fib(0, 0). fib(1, 1). fib(N, F) :- N != 1, N != 0, N1 :- -(N, 1), N2 :- -(N, 2), fib(N1, F1), fib(N2, F2), F :- +(F1, F2). fib(30, F)?
The compilation of this module builds up suitable data structures and
generates calls to run-time support functions. Those support functions
implement Datalog’s evaluation model, and they are part of the
datalog language just as much as its parser. At the
same time, the underlying Racket module and binding machinery makes it
easy for Datalog programs to refer to bindings from other Racket
modules like racket/base—
Finally, the following program does not produce the value 832040. Instead, it produces a representation of a document that starts basically the same as the of the document that you’re currently reading. In scribble/manual, the default mode is literal text, and a @ character plays a role similar to \ in LaTeX (except that @ in Scribble escapes to a real programming language).
#lang scribble/manual @(require (for-label racket/base)) @title{Example Racket Languages} Every Racket program starts with @hash-lang[] and the name of a language. The language determines the way the rest of the file is interpreted. Here's an example Racket program that prints @racketresultfont{832040} when run: @codeblock{ #lang racket (define (fib n) (cond [(= n 0) 0] [(= n 1) 1] [else (+ (fib (- n 1)) (fib (- n 2)))])) (fib 30) }
Here, again, the evaluation model for scribble/manual is significantly different than the evaluation of racket/base. More than datalog, however, scribble/manual takes advantage of an easy escape to Racket’s evaluation model.