The book defines block structure in Chapter 1, allowing you to 'package' define
s inside a procedure definition.
Consider this mean-square
definition for example:
(define (mean-square x y)
(define (square x) (* x x))
(define (average x y) (/ (+ x y) 2))
(average (square x) (square y)))
when I run (mean-square 2 4)
I correctly get 10
.
My question is, are the internal definitions ( square
and average
in this toy case ) run each time I invoke the mean-square
procedure via the interpreter? If so, isn't that inefficient? and if not, why?
If the code is somewhat naively compiled, there could be some overhead. The reason is that the inner functions are defined in a brand new lexical environment that is freshly instantiated on each entry into the function. In the abstract semantics, each time the function is called, new lexical closures have to be captured and wired into the correct spots in that environment frame.
Thus it boils down to how much of this can the compiler optimize away. For instance it can notice that neither of the functions actually references the surrounding lexical environment. (The x
and y
references in these functions are to their own parameters, not to those of the surrounding mean-square
). Which means they both be moved to the top level without changing semantics:
(define (__anon1 x) (* x x))
(define (__anon2 x y) (/ (+ x y) 2))
(define (mean-square x y)
(define square __anon1)
(define average __anon2)
(average (square x) (square y)))
And since now square
and average
are effectively simple aliases (aliases for global entities that are generated by the compiler, which the compiler knows aren't being manipulated by anything outside of its control), the values they denote can be propagated through:
(define (mean-square x y)
(__anon2 (__anon1 x) (__anon1 y)))