An identifier is a name that appears in a program. The meaning of the name is determined by the identifier’s binding, which is an association between an identifier and some other item—e.g., a specific value, a function, a macro, or another identifier.
An identifier with a binding is also known as a variable:
(define prometheus "I am bound, ergo I am a variable")
prometheus
"I am bound, ergo I am a variable"
An identifier without a binding is called an unbound identifier. A Racket program cannot run unless every identifier has a binding, so this is a phrase commonly seen in error messages:
atlas
atlas: unbound identifier in module in: atlas
An identifier can be written with any Unicode characters, except whitespace and the following reserved characters:
( ) [ ] { } " , ' ` ; # | \
Numbers can’t be used as identifiers either:
(define 42a "value") ; ok, though possibly confusing
(define 42 "value") ; not ok: bad-syntax error
By convention, identifiers are typically written in lowercase, with hyphens between words:
lowercased
lowercased-with-hyphens
Naming styles common in other languages are not syntactically wrong in Racket, but they’re also not idiomatic:
Capitalized ; no
lowercased_with_underscores ; no
Title-Cased ; no
CamelCased ; hell no
Certain classes of functions have recommended naming conventions. See functions.
Identifier bindings can be created three ways:
With an explicit binding form like define, let, or lambda. These bindings will override (aka shadow) existing bindings outside the form:
(define x 42)
x ; 42
(define y 100)
(let ([y 42])
(+ x y)) ; 84 (not 142)
(define z 1000)
(define f (lambda ([z 42]) (+ x y z)))
(f) ; 184 (not 1142)
By importing bindings from another module with a form like require. Bindings within the module will shadow imported bindings:
(module mod1 br
(define foo "zim")
(provide foo))
(require (submod "." mod1))
foo ; "zim"
(module mod2 br
(define bar "zam")
(provide bar))
(require (submod "." mod2))
(define bar "bang")
bar ; "bang" (not "zam")
But an error will arise if two imported modules provide bindings for the same identifier:
(module mod1 br
(define foo "zim")
(provide foo))
(require (submod "." mod1))
(module mod2 br
(define foo "zam")
(provide foo))
(require (submod "." mod2))
;; error: identifier `foo` imported twice
By making new bindings with a macro. Macros can attach bindings to existing identifiers, or create a new identifier and its binding.
(define-macro (define-as-42 ID)
#'(define ID 42))
(define-as-42 foo)
foo ; 42
(define-macro (define-bar-as-42)
(with-pattern ([BAR-ID (datum->syntax caller-stx 'bar)])
#'(define BAR-ID 42)))
(define-bar-as-42)
bar ; 42
Every identifier binding has a scope that determines where in the program the binding is visible. Outside that scope, the binding won’t work. The scope of a binding depends on how & where the identifier is introduced. The three most common options:
Expression scope: a binding created within another expression is only visible within that expression (and it can be shadowed within that expression):
(define (get-bar)
(define bar 42)
(let ([bar 92])
bar))
(get-bar) ; 92
bar ; error: unbound identifier
Module scope: a binding created at the top level of a module with define, or imported with require, is visible throughout the module (though it can be shadowed elsewhere). + True, a module is itself an expression. But module scope is not a special case of expression scope, because bindings can only cross module boundaries when explicitly requested (for instance, with require).
(define top 40)
(define (f1)
(let ()
(let ()
(let ()
top))))
(f1) ; 40
(define (f2)
(let ([top 41])
(let ([top 42])
(let ([top 43])
top))))
(f2) ; 43
Macro scope: if a macro creates an identifier and its binding (i.e., a variable) it’s only visible to other code created by the same macro. Caution: this is a trap for the unwary. In the example below, even though the macro emits (define foo 42)—which sure looks like it will create a variable foo that has module scope—it doesn’t. That variable is not visible to code outside the macro:
(define-macro (make-macro-scoped-foo)
#'(begin
(define foo 42)
(* foo 2)))
(make-macro-scoped-foo) ; 84
foo ; unbound identifier (!)
But if a macro adds a binding to an existing identifier—for instance, one passed as an argument to the macro—that binding does not have macro scope. Instead, it adopts the scope of the identifier:
(define-macro (bind-existing-identifier ID)
#'(begin
(define ID 42)
(* ID 2)))
(bind-existing-identifier foo) ; 84
foo ; 42 (and `foo` has module scope)
(let ()
(bind-existing-identifier bar)
bar) ; 42 (and `bar` has expression scope)
bar ; unbound identifier
For more about this behavior—which is a feature, not a bug—see hygiene.
Identifiers and Binding in the Racket Guide
Identifiers, Binding, and Scopes in the Racket Reference