4.2 Identifiers and Binding

The context of an expression determines the meaning of identifiers that appear in the expression. In particular, starting a module with the language racket, as in

  #lang racket

means that, within the module, the identifiers described in this guide start with the meaning described here: cons refers to the function that creates a pair, car refers to the function that extracts the first element of a pair, and so on.

+Symbols introduces the syntax of identifiers.

Forms like define, lambda, and let associate a meaning with one or more identifiers; that is, they bind identifiers. The part of the program for which the binding applies is the scope of the binding. The set of bindings in effect for a given expression is the expression’s environment.

For example, in

  #lang racket
  
  (define f
    (lambda (x)
      (let ([y 5])
        (+ x y))))
  
  (f 10)

the define is a binding of f, the lambda has a binding for x, and the let has a binding for y. The scope of the binding for f is the entire module; the scope of the x binding is (let ([y 5]) (+ x y)); and the scope of the y binding is just (+ x y). The environment of (+ x y) includes bindings for y, x, and f, as well as everything in racket.

A module-level define can bind only identifiers that are not already bound within the module. For example, (define cons 1) is a syntax error in a racket module, since cons is provided by racket. A local define or other binding forms, however, can give a new local binding for an identifier that already has a binding; such a binding shadows the existing binding.

Examples:

  (define f
    (lambda (append)
      (define cons (append "ugly" "confusing"))
      (let ([append 'this-was])
        (list append cons))))
  > (f list)

  '(this-was ("ugly" "confusing"))

Even identifiers like define and lambda get their meanings from bindings, though they have transformer bindings (which means that they indicate syntactic forms) instead of value bindings. Since define has a transformer binding, the identifier define cannot be used by itself to get a value. However, the normal binding for define can be shadowed.

Examples:

  > define

  eval:1:0: define: bad syntax in: define

  > (let ([define 5]) define)

  5

Shadowing standard bindings in this way is rarely a good idea, but the possibility is an inherent part of Racket’s flexibility.