On this page:
1.1 Racket Variants
1.2 Changing the Surface Syntax
1.3 Changing the Evaluation Model
7.4.0.4

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/basewhich is possibly only because racket/base defined requirein the same way that require can be used to import a library’s functions:

#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/baseeven bindings that are implemented in other Racket languages.

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.