Scheme supports the definition of variables in different contexts. Variables can be defined at the top level, so that they are visible in the entire program, and variables can be defined locally to procedures and expressions. This is important for modularity and data abstraction.
On the top level of a program (i.e. when not inside the body of a
procedure definition or a let, let* or letrec
expression), a definition of the form
(define a value)
defines a variable called a and sets it to the value value.
If the variable already exists, because it has already been created by a
previous define expression with the same name, its value is
simply changed to the new value. In this case, then, the above
form is completely equivalent to
(set! a value)
This equivalence means that define can be used interchangeably
with set! to change the value of variables at the top level of
the REPL or a Scheme source file. It is useful during interactive
development when reloading a Scheme file that you have modified, because
it allows the define expressions in that file to work as expected
both the first time that the file is loaded and on subsequent occasions.
Note, though, that define and set! are not always
equivalent. For example, a set! is not allowed if the named
variable does not already exist, and the two expressions can behave
differently in the case where there are imported variables visible from
another module.
define expression is unspecified.
The C API equivalents of define are scm_define and
scm_c_define, which differ from each other in whether the
variable name is specified as a SCM symbol or as a
null-terminated C string.
define, with variable name specified either by
sym, a symbol, or by name, a null-terminated C string. Both
variants return the new or preexisting variable object.
define (when it occurs at top level), scm_define and
scm_c_define all create or set the value of a variable in the top
level environment of the current module. If there was not already a
variable with the specified name belonging to the current module, but a
similarly named variable from another module was visible through having
been imported, the newly created variable in the current module will
shadow the imported variable, such that the imported variable is no
longer visible.
Attention: Scheme definitions inside local binding constructs (see section Local Variable Bindings) act differently (see section Internal definitions).
As opposed to definitions at the top level, which are visible in the whole program (or current module, when Guile modules are used), it is also possible to define variables which are only visible in a well-defined part of the program. Normally, this part of a program will be a procedure or a subexpression of a procedure.
With the constructs for local binding (let, let* and
letrec), the Scheme language has a block structure like most
other programming languages since the days of ALGOL 60. Readers
familiar to languages like C or Java should already be used to this
concept, but the family of let expressions has a few properties
which are well worth knowing.
The first local binding construct is let. The other constructs
let* and letrec are specialized versions for usage where
using plain let is a bit inconvenient.
((variable1 init1) ...)
that is zero or more two-element lists of a variable and an arbitrary expression each. All variable names must be distinct.
A let expression is evaluated as follows.
let
expression.
The init expressions are not allowed to refer to any of the variables.
let, but the variable bindings are performed
sequentially, that means that all init expression are allowed to
use the variables defined on their left in the binding list.
A let* expression can always be expressed with nested let
expressions.
(let* ((a 1) (b a))
b)
==
(let ((a 1))
(let ((b a))
b))
let, but it is possible to refer to the variable
from lambda expression created in any of the inits. That is,
procedures created in the init expression can recursively refer to
the defined variables.
(letrec ((even?
(lambda (n)
(if (zero? n)
#t
(odd? (- n 1)))))
(odd?
(lambda (n)
(if (zero? n)
#f
(even? (- n 1))))))
(even? 88))
=>
#t
There is also an alternative form of the let form, which is used
for expressing iteration. Because of the use as a looping construct,
this form (the named let) is documented in the section about
iteration (see section Iteration mechanisms)
A define form which appears inside the body of a lambda,
let, let*, letrec or equivalent expression is
called an internal definition. An internal definition differs
from a top level definition (see section Top Level Variable Definitions), because the definition
is only visible inside the complete body of the enclosing form. Let us
examine the following example.
(let ((frumble "froz")) (define banana (lambda () (apple 'peach))) (define apple (lambda (x) x)) (banana)) => peach
Here the enclosing form is a let, so the defines in the
let-body are internal definitions. Because the scope of the
internal definitions is the complete body of the
let-expression, the lambda-expression which gets bound
to the variable banana may refer to the variable apple,
even though it's definition appears lexically after the definition
of banana. This is because a sequence of internal definition
acts as if it were a letrec expression.
(let () (define a 1) (define b 2) (+ a b))
is equivalent to
(let ()
(letrec ((a 1) (b 2))
(+ a b)))
Another noteworthy difference to top level definitions is that within
one group of internal definitions all variable names must be distinct.
That means where on the top level a second define for a given variable
acts like a set!, an exception is thrown for internal definitions
with duplicate bindings.
Guile provides a procedure for checking whether a symbol is bound in the
top level environment. If you want to test whether a symbol is locally
bound in expression, you can use the bound? macro from the module
(ice-9 optargs), documented in section Optional Arguments.
#t if sym is defined in the lexical environment env. When env is not specified, look in the top-level environment as defined by the current module.
Go to the first, previous, next, last section, table of contents.