Comp 210 Lab 7: local

First, to use local, change DrScheme's language level to "Intermediate Student".

Index: What is local?, Scope, When to use local?, When not to use local?, local & the Design Methodology

Note to labbies: This lab includes a lot of text, much of it review. Rather than echoing it all, emphasize the examples and exercises. Plan ahead on how to deliver the examples without writing everything out.


Review -- What is local?

Let's start with a quick review of local, as introduced in class. Its syntax is

     (local [definitions] expression)
for example,
     (local [(define x 3)
             (define y (+ x 1))]
        (+ x y))

How do we evaluate it? Basically, we

  1. evaluate the definitions' expressions, in sequence,
  2. make the resulting definitions global, and
  3. evaluate the local's expression body.
There is one problem with this: the locally defined names may conflict with previous definitions. How do we disambiguate which definitions are being referred to? One way to explain this is that we'll rename the local variables consistently, something like x to x', so that the resulting names have not been used before and will not be used again. So, we amend the previous evaluation strategy to
  1. rename the locally defined variables consistently in both the definitions' expressions and the local's expression body,
  2. evaluate the definitions' expressions, in sequence,
  3. make the resulting definitions global, and
  4. evaluate the local's expression body.

local does not allow you to solve any problems that you couldn't solve before, but it does allow you to solve them with better programs.

Evaluation exercise

Hand-evaluate all steps of the following program:

     (define x 3)
     (define z 6)
     (local [(define x 7)
             (define y (+ x 4))]
        (+ x y z))

For the curious... You can also use define-struct in a local.


Scope

Previously, in programs like

     (define x 3)
     (define (cube x)
        (* x x x))
     (cube 4)
we knew that the variable x inside cube was somehow different from the one outside. We didn't discuss this much, since it was fairly intuitive. Our terminology is that the x inside the function is local, while the other is global. We also say that the local variable shadows (or hides or masks) the identically-named global variable.

If you use DrScheme's "Check Syntax" button, it will show you which use corresponds to which definition.

DrScheme exercise
Use DrScheme's "Check Syntax" with the cube example, and look at the various arrows.

This distinction is one of scope. I.e., we can have multiple distinct variables with the same name. Each variable has a scope -- the part of the code where that variable can be referred to. Previously, we variables either had local scope or global scope. Now, local allows us to nest definitons and make further distinctions. I.e., some variables will be "more local" than others.

Thinking about scope is basically a shortcut to the whole renaming step of local evaluation. Once you understand scope, you simply say that a use of variable x refers to the "most local" definition of x. The scoping rules are rather simple:

Scoping exercises

For each of the following,

  1. draw an arrow between each variable use and definition,
  2. figure out what are the results of the expressions, and
  3. check your answers with DrScheme's "Check Syntax" button and its evaluation.

     ; For use in each example:
     (define x 1)
     (define y 2)
     (define z 3)


     ; Example 1:
     (define (fee x y)
        (local [(define z 7)
                (define y 4)]
           (+ x y z)))
     (fee x z)


     ; Example 2:
     (define (fie x y)
        (local [(define z 7)
                (define y (+ y 3))]
           (+ x y z)))
     (fie x z)


     ; Example 3:
     (define (foe1 x y)
        (local [(define z 7)
                (define y (+ x z))
                (define x (local [(define y 10)] (+ y z)))]
           (+ x y z)))
     (foe1 x z)

     ; Example 4:
     (define (foe2 x y)
        (local [(define z 7)
                (define x (local [(define y 10)] (+ y z)))
                (define y (+ x z))]
           (+ x y z)))
     (foe2 x z)


     ; Example 5:
     (define (fum x y)
        (local [(define fum 7)
                (define (x z) (+ y z))]
           (x fum)))
     (fum x z)


     ; Example 6:
     (define (foo x y)
        (local [(define (z y)
                    (cond [(zero? y) 1]
                          [(positive? y) (* x (z (sub1 y)))]))]
           (local [(define x (+ y 1))]
              (+ x (z y)))))
     (foo x z)

Fortunately, real-world examples as convoluted as these are uncommon. However, the last example's use of x in z is a common technique we'll see more of later.


When to use local?

There are several overlapping reasons for using local, as described in class:

To do: Go back and review your last two assignments. Use local where appropriate.

You are expected to use local on the current and future assignments, where appropriate.


When not to use local?


local & the Design Methodology

You should still use the design methodology for developing local functions. In particular, local functions still need a contract and purpose.

However, there are complications. You can't test a local function independently, because it is hidden. A standard technique is to define the function globally for testing, then move it into a local. This technique doesn't work when the function uses variables that aren't local to the function, as when eliding invariant arguments, e.g.,

     ; expt : nat nat -> nat
     ; Returns x to the y power.
     (define (expt x y)
        (local [; expt-of-x : nat -> nat
                ; Returns x to the y power.
                (define (expt-of-x y)
                    (cond
                        [(zero? y)     1]
                        [(positive? y) (* x (expt-of-x (sub1 y)))]))]
           (expt-of-x y)))