
Tom's Lisp Definitions and Scope
As noted in the input section,
identifiers have values which are assigned.
The most straightforward way is this:
lsp>(set 'fred 495)
fred
lsp>fred
495
lsp>(set 'fred (cdr '(some days it rains)))
fred
lsp>fred
(days it rains)
The identifier sent to set must be quoted. This is because
all function arguments are evaluated before they are sent, so
trying to set a an undefined name does not work well:
lsp>(set zomba 17)
**** Error 1: Undefined zomba ****
(set zomba 17)
The name zomba must be evaluated before it can be sent to
set, and this evaluation fails.
Of course, it can be even more
interesting if zomba does have a value:
lsp>(set 'zomba 'fred)
zomba
lsp>(set zomba 17)
fred
lsp>zomba
fred
lsp>fred
17
Here, the second set looks as if it is assigning zomba, but it is
actually assigning fred, since that is the value of zomba.
In any case, the more usual ways of assigning values are:
lsp>(setq boris 56)
boris
lsp>(define mike 34)
mike
lsp>boris
56
lsp>mike
34
The setq operation automatically quotes the name;
this form of define, using a plain name instead of a list,
is the same as setq.
We previously used
define to create functions, be we can also use plain old
setq. As noted in the
functions section, lambda creates
anonymous functions. The set operations gives names to things.
So, what happens when you name an anonymous function?
You get a named function, just like the ones define creates.
lsp>(setq napple (lambda (no2 lis) (cons (car lis) (cons no2 (cdr lis)))))
napple
lsp>(napple 'wump '(a b c d e))
(a wump b c d e)
Names belong to a scope.
The scope of a variable is the portion of the code where it can
be used.
Scopes are familiar from
other languages: the { and } symbols delimit a scope
in C/C++/Java. Variables created in a scope disappear at its
end. If a name is declared in a scope which matches a name
available outside it, the new definition covers the old one to
the end of the scope, after which the outer definition reappears.
In Tom's Lisp, the program starts in a main scope, and
a new scope is introduced by the scope operator.
It takes an arbitrary set of forms and evaluates them left to
right. These are evaluated in a new scope which ends with the
closing ) of the scope construct.
The value of the scope is the value of the last form.
lsp>(setq cute 78)
cute
lsp>cute
78
lsp>ugly
**** Error 1: Undefined ugly ****
lsp>(scope (setq ugly 23) ugly)
23
lsp>ugly
**** Error 1: Undefined ugly ****
lsp>(scope (setq cute 100) (* 2 cute))
200
lsp>cute
78
lsp>(* cute 2)
156
In the first scope call, ugly is defined to be 23, but that
definition vanishes at the end of the scope call.
In the second, cute is given a definition of 100 which covers the
definition of 78 in the main scope. When the scope form
finishes, the value of cute reverts to 78.
Contexts are created and searched in a stack-like manner.
When a scope is entered, it is pushed atop
the current scope. When the scope exits, it is popped, so the
previous current scope is current again. To find a variable,
the system checks the current scope, then moves down the stack
until the variable is found.
Scopes for Function Evaluation
Functions also introduce new scopes. When a function is run,
its parameters are defined in a new scope. Also, when a function is
created, it remembers the scope where it was created, and
runs the body in that scope, regardless of the scope from which it
is called. This arrangement
is called static scoping, and is used by most programming
languages. Specifically, a function call:
- Evaluates the arguments in the scope of the function call.
- Enters a new scope, but stacks it atop the one where the
function was created, not where it was called and its parameters
evaluated.
- Binds the argument values to the parameter names in
this new scope.
- Evaluates the function body.
- Leaves the scope and returns the value produced by the
last expression in the body.
For instance:
lsp>(setq inc-amt 10)
inc-amt
lsp>(define (incr x) (+ x inc-amt))
incr
lsp>(incr 5)
15
lsp>(scope (setq inc-amt 250) (incr 5))
15
lsp>(scope (setq inc-amt 250) (+ 5 inc-amt)))
255
Here, the function incr simply adds its parameter to the
value of inc-amt. Since the function is created in the main
context, the inc-amt in the
function body is evaluated in that scope. We enter a new scope,
set inc-amt to 250 and
call (incr 5). The function returns 15, since it was
defined in the main scope where inc-amt has the value 10.
When we refer to inc-amt directly in the second scope call
(rather than from the body of incr), it has the value 250.
Let
Another way to get scopes in Tom's Lisp is the let construct.
The let operation is somewhat traditional in Lisp.
In Tom's, it takes the form:
(let ((var1 val1) . . . (varn valn)) form)
This evaluates the form after entering a new scope and
setting each variable
vari to its value vali.
lsp>(let ((fred 10) (barney (+ fred 5)) (alice "ok"))
--->(cons (* fred barney) alice))
(150 . "ok")
This is the same as:
lsp>(scope (setq fred 10) (setq barney (+ fred 5)) (setq alice "ok")
--->(cons (* fred barney) alice))
(150 . "ok")
Tom's Lisp uses a fairly simple scope model. Production Lisp interpreters
may have somewhat greater complexity.
Helper Functions
One application of scope rules is the creation of private helper functions.
Here's one way to do it:
(define (sumsqr x y)
(begin
(define (sqr x) (* x x))
(+ (sqr x) (sqr y))
)
)
The begin construct simply executes each member form in order,
left to right.
lsp>(sumsqr 9 18)
405
lsp>(sqr 3)
**** Error 1: Undefined sqr ****
(sqr 3)
lsp>(sumsqr 5 6)
61
The function sqr is created in sumsqr's own scope each time
sumsqr is run. It is destroyed when sumsqr exits, and
is never visible anywhere else.
Defining a function within a function requires the inner (private) definition
to be evaluated each time the outer (public) function is called.
An alternative is to define the functions inside a scope. When the public
function runs, it has access to the contents of this scope, but nothing
else does.
(setq sumsqr
(scope
(define (sqr x) (* x x))
(lambda (x y) (+ (sqr x) (sqr y)))
)
)
lsp>(sumsqr 3 4)
25
lsp>(sumsqr 10 12)
244
lsp>sqr
**** Error 1: Undefined sqr ****
lsp>
Here the sqr function is defined inside the scope. The last
expression of the scope is the lambda operation. Its value
becomes the
value of the scope and is assigned to sumsqr. When
sumsqr is run, it is run in that scope where the function
was created, and has access to sqr.
But since the scope has exited, the sqr is not available elsewhere.