RU beehive logo ITEC dept promo banner
ITEC 380
2008fall
ibarland

homeinfolecturesexamsarchive

lect08b
scope

Reading from the book: §5.8-§5.11.

We have already seen that Scheme has a let statement, which can be implemented in terms of a function-call:

(let {[a 2]
      [b 4]}
   (+ 3 a b))

=
((lambda (a b) (+ 3 a b))
 2 4)
Quiz: If we give a definition for a outside the let, what does it return? Moreover, what do you expect it to return?:
(define a 1)

(let {[a 2]
      [b (sqr a)]}
   (+ 3 a b))
What if we wanted the other version?

Scheme does happen to provide let*. let* can be implemented by expanding it into nested lets.1 Here's an example:

(define a 1)

(let* {[a 2]
       [b (+ sqr a)]}
   (+ 3 a b))

=     ; Rewrite as nested lets:
(let {[a 2]}
  (let {[b (sqr a)]}
   (+ 3 a b)))

=     ; Which, following the already-known rules, becomes:
((lambda (a)
   (let {[b (sqr a)]}
      (+ 3 a b)))
 2)

=   ; Note how there's no confusion about using the global a=1.

(let {[b (sqr 2)]}
  (+ 3 2 b))

= 

(let {[b 4]}
  (+ 3 2 b))


=    ; Now, follow regular ol' rules for evaluation a function-application:

   ((lambda (b)
       (+ 3 2 b))
    4)

=  (+ 3 2 4)

=  9

Finally, what about a version where the scope includes the current let-branch? For variables, this is never desirable ('variable used before initialized'), but it is necessary for recursive functions:

(define (insert-sort lon)
  (letrec {[insert (lambda (n nums)
                     (cond [(empty? nums) (list n)]
                           [(cons? nums)  (if (< n (first nums))
                                              (cons n nums)
                                              (cons (first nums) (insert n (rest nums))))]))]}
    (foldl insert empty lon)))




; Another example, using mutual recursion:
(letrec {[a 2]
         [even? (lambda (n) (or (zero? n) (odd? (sub1 n))))]
         [odd?  (lambda (n) (not (even? n)))]}
  (even? a))
In particular, look closely at insert-sort: Because insert is just a local variable, it has effectively made its helper-function private.


Let's ask these same questions in Java:

class Foo {
  static int a = 1;

  static int lala() {
    int a = 2;
    int b = a*a;
    return 3+a+b;
    }
  }
Note that Java uses letrec-like semantics for fields, and let-like semantics for local variables. (That's fine.) But what if we swapped the order of the locals a,b?
class Foo {
  static int a = 1;

  static int lala() {
    int b = a*a;
    int a = 2;
    return 3+a+b;
    }
  }
Going further, what exactly is the scope of a parameter, in Java?
class Foo {
  static int a = 1;

  static int lala( int a ) {
    int b = a*a;
    int a = a;
    return 3+a+b;
    }
  }
Compile to find out!

Two possible approaches:

  1. The scope of a variable is everywhere in the block (and it is undefined if you try to access it before its initialized);
  2. The scope of a variable is from its declaration to the end of the current block.
    (In which case: does it contain its own right-hand-side? That is int a = a+1; an error, or using an a from surrounding code?)
There are languages which use each of these two approaches! (And some like scheme which let you choose, and use keywords to make it clear that such a choice is being made.)


1 (Therefore, let* isn't really a primitive scoping mechanism; it's just convenient syntactic sugar. In the same way that let itself can be thought of as syntactic sugar for passing to a parameter.)      

homeinfolecturesexamsarchive


©2008, Ian Barland, Radford University
Last modified 2008.Oct.29 (Wed)
Please mail any suggestions
(incl. typos, broken links)
to iba�rlandrad�ford.edu
Powered by PLT Scheme