Programming Languages Lexical Scope and Function Closures Adapted - - PowerPoint PPT Presentation

programming languages
SMART_READER_LITE
LIVE PREVIEW

Programming Languages Lexical Scope and Function Closures Adapted - - PowerPoint PPT Presentation

Programming Languages Lexical Scope and Function Closures Adapted from Dan Grossman's PL class, U. of Washington Very important concept We know that the body of a function can refer to non-local variables i.e., variables that are not


slide-1
SLIDE 1

Programming Languages Lexical Scope and Function Closures

Adapted from Dan Grossman's PL class,

  • U. of Washington
slide-2
SLIDE 2

Very important concept

  • We know that the body of a function can refer to non-local

variables – i.e., variables that are not explicitly defined in that function or passed in as arguments

  • So how does a language know where to find values of non-local

variables? Look where the function was defined (not where it was called)

  • There are lots of good reasons for this semantics

– Discussed after explaining what the semantics is

  • For HW, exams, and competent programming, you must “get this”
  • This concept is called lexical scope (sometimes also called static

scope)

Spring 2013 2 Programming Languages

slide-3
SLIDE 3

Example

Spring 2013 3 Programming Languages

  • 1- (define x 1)
  • 2- (define (f y) (+ x y))
  • 3- (define y 4)
  • 4- (define z (let ((x 2)) (f (+ x y))))
  • Line 2 defines a function that, when called, evaluates body

(+ x y) in environment where x maps to 1 and y maps to the argument

  • Call on line 4:

– Creates a new environment where x maps to 2. – Looks up f to get the function defined on line 2. – Evaluates (+ x y) in the new environment, producing 6 – Calls the function, which evaluates the body in the old environment, producing 7

slide-4
SLIDE 4

Closures

How can functions be evaluated in old environments? – The language implementation keeps them around as necessary Can define the semantics of functions as follows:

  • A function value has two parts

– The code (obviously) – The environment that was current when the function was defined

  • This value is called a function closure or just closure.
  • When a function f is called, f's code is evaluated in the environment

pointed to by f's environment pointer. – (The environment is first extended with extra bindings for the values of f's arguments.)

Spring 2013 4 Programming Languages

slide-5
SLIDE 5

Example

Spring 2013 5 Programming Languages

  • Line 2 creates a closure and binds f to it:

– Code: “take argument y and have body (+ x y)” – Environment: “x maps to 1”

  • (Plus whatever else has been previously defined,

including f for recursion)

  • 1- (define x 1)
  • 2- (define (f y) (+ x y))
  • 3- (define y 4)
  • 4- (define z (let ((x 2)) (f (+ x y))))
slide-6
SLIDE 6

What's happening behind the scenes

  • An environment is stored using frames.
  • A frame is a table that maps variables to values; a frame also

may have a single pointer to another frame.

  • When a variable is asked to be looked up in an "environment,"

the lookup always starts in some frame.

  • If the variable is not found in that frame, the search continues

wherever the frame points to (another frame).

  • If the search ever gets to a frame without a pointer to another

frame (usually this is the "global" or "top-level" frame), we report an error that the variable is undefined.

Spring 2013 6 Programming Languages

slide-7
SLIDE 7

Spring 2013 7 Programming Languages

  • 1- (define x 1)
  • 2- (define (f y) (+ x y))
  • 3- (define y 4)
  • 4- (define z (let ((x 2)) (f (+ x y))))

global

slide-8
SLIDE 8

Spring 2013 8 Programming Languages

  • 1- (define x 1)
  • 2- (define (f y) (+ x y))
  • 3- (define y 4)
  • 4- (define z (let ((x 2)) (f (+ x y))))

global x 1

slide-9
SLIDE 9

Spring 2013 9 Programming Languages

  • 1- (define x 1)
  • 2- (define (f y) (+ x y))
  • 3- (define y 4)
  • 4- (define z (let ((x 2)) (f (+ x y))))

global x 1
 f

  • args: y


code: (+ x y)

slide-10
SLIDE 10

Rules for frames and environments

  • Rule 1:

– Every function definition (including anonymous function definitions) creates a closure where

  • the code part of the closure points to the function's code
  • the environment part of the closure points to the frame

that was current when the function was defined (the frame we are currently using to look up variables)

Spring 2013 10 Programming Languages

global x 1
 f

  • args: y


code: (+ x y)

slide-11
SLIDE 11

Rules for frames and environments

  • Rule 2:

– Every function call creates a new frame consisting of the following:

  • the new frame's table has bindings for all of the function's

arguments and their corresponding values

  • the new frame's pointer points to the same environment

that f's environment pointer points to.

Spring 2013 11 Programming Languages

slide-12
SLIDE 12

Spring 2013 12 Programming Languages

  • 1- (define x 1)
  • 2- (define (f y) (+ x y))
  • 3- (define q (f 5)) ; changed this line

global x 1
 f

  • args: y


code: (+ x y)

slide-13
SLIDE 13

Spring 2013 13 Programming Languages

  • 1- (define x 1)
  • 2- (define (f y) (+ x y))
  • 3- (define q (f 5)) ; changed this line

global x 1
 f

  • args: y


code: (+ x y)

f y 5


slide-14
SLIDE 14

Spring 2013 14 Programming Languages

  • 1- (define x 1)
  • 2- (define (f y) (+ x y))
  • 3- (define q (f 5)) ; changed this line

global x 1
 f q 6

args: y
 code: (+ x y)

f y 5


slide-15
SLIDE 15

So what?

Now you know the rules. Next steps:

  • (Silly) examples to demonstrate how the rule works for higher-
  • rder functions
  • Why the other natural rule, dynamic scope, is a bad idea
  • Powerful idioms with higher-order functions that use this rule

– This lecture: Passing functions to functions like filter – Next lecture: Several more idioms

Spring 2013 15 Programming Languages

slide-16
SLIDE 16

Example: Returning a function

  • Trust the rules:

– Evaluating line 2 binds f to a closure. – Evaluating line 3 binds g to a closure as well.

  • New frame is created for the call to f.

– Evaluating line 4 binds z to a number.

  • New frame is created for the call to g.

Spring 2013 17 Programming Languages

1 (define x 1) 2 (define (f y) (lambda (z) (+ x y z))) 3 (define g (f 4)) 4 (define z (g 6))

slide-17
SLIDE 17

Spring 2013 18 Programming Languages

1 (define x 1) 2 (define (f y) (lambda (z) (+ x y z))) 3 (define g (f 4)) 4 (define z (g 6))

global 


slide-18
SLIDE 18

Spring 2013 19 Programming Languages

1 (define x 1) 2 (define (f y) (lambda (z) (+ x y z))) 3 (define g (f 4)) 4 (define z (g 6))

global x 1
 f

  • args: y


code: (lambda (z)...)

slide-19
SLIDE 19

Spring 2013 20 Programming Languages

1 (define x 1) 2 (define (f y) (lambda (z) (+ x y z))) 3 (define g (f 4)) 4 (define z (g 6))

global x 1
 f

  • args: y


code: (lambda (z)...)

f y 4

slide-20
SLIDE 20

Spring 2013 21 Programming Languages

1 (define x 1) 2 (define (f y) (lambda (z) (+ x y z))) 3 (define g (f 4)) 4 (define z (g 6))

global x 1
 f

  • args: y


code: (lambda (z)...)

f y 4

args: z
 code: (+ x y z)

slide-21
SLIDE 21

Spring 2013 22 Programming Languages

1 (define x 1) 2 (define (f y) (lambda (z) (+ x y z))) 3 (define g (f 4)) 4 (define z (g 6))

global x 1
 f g

  • args: y


code: (lambda (z)...)

f y 4

args: z
 code: (+ x y z)

slide-22
SLIDE 22

Spring 2013 23 Programming Languages

1 (define x 1) 2 (define (f y) (lambda (z) (+ x y z))) 3 (define g (f 4)) 4 (define z (g 6))

global x 1
 f g z 11

args: y
 code: (lambda (z)...)

f y 4 g z 6

args: z
 code: (+ x y z)

slide-23
SLIDE 23

Rules for frames and environments

  • Rule 2a:

– Every evaluation of a "let" expression creates a new frame as follows:

  • the new frame's table has bindings for all of the let

expressions variables and their corresponding values

  • the new frame's pointer points to the frame where the let

expression was defined

Spring 2013 24 Programming Languages

slide-24
SLIDE 24

Example: Passing a function

  • Trust the rules:

– Evaluating line 1 binds f to a closure. – Evaluating line 2 binds x to 4. – Evaluating line 3 binds h to a closure. – Evaluating line 4 binds z to a number.

  • First, calls f (creates new frame), then evaluates

"let" (creates a new frame), then calls g (creates a new frame).

Spring 2013 25 Programming Languages

1 (define (f g) (let ((x 3)) (g 2))) 2 (define x 4) 3 (define (h y) (+ x y z)) 4 (define z (f h))

slide-25
SLIDE 25

Spring 2013 26 Programming Languages

1 (define (f g) (let ((x 3)) (g 2))) 2 (define x 4) 3 (define (h y) (+ x y)) 4 (define z (f h))

global 


slide-26
SLIDE 26

Spring 2013 27 Programming Languages

1 (define (f g) (let ((x 3)) (g 2))) 2 (define x 4) 3 (define (h y) (+ x y)) 4 (define z (f h))

global f 
 x 4

  • args: y


code: (let ((x...

slide-27
SLIDE 27

Spring 2013 28 Programming Languages

1 (define (f g) (let ((x 3)) (g 2))) 2 (define x 4) 3 (define (h y) (+ x y)) 4 (define z (f h))

global f 
 x 4

  • args: y


code: (let ((x... args: y
 code: (+ x y)

slide-28
SLIDE 28

Spring 2013 29 Programming Languages

1 (define (f g) (let ((x 3)) (g 2))) 2 (define x 4) 3 (define (h y) (+ x y)) 4 (define z (f h))

global f 
 x 4 h

  • args: y


code: (let ((x... args: y
 code: (+ x y)

slide-29
SLIDE 29

Spring 2013 30 Programming Languages

1 (define (f g) (let ((x 3)) (g 2))) 2 (define x 4) 3 (define (h y) (+ x y)) 4 (define z (f h))

global f 
 x 4 h

  • args: y


code: (let ((x...

f g

args: y
 code: (+ x y)

slide-30
SLIDE 30

Spring 2013 31 Programming Languages

1 (define (f g) (let ((x 3)) (g 2))) 2 (define x 4) 3 (define (h y) (+ x y)) 4 (define z (f h))

global f 
 x 4 h

  • args: y


code: (let ((x...

f g

args: y
 code: (+ x y)

let x 3

slide-31
SLIDE 31

Spring 2013 32 Programming Languages

1 (define (f g) (let ((x 3)) (g 2))) 2 (define x 4) 3 (define (h y) (+ x y)) 4 (define z (f h))

global f 
 x 4 h z 6

args: y
 code: (let ((x...

f g g y 2

args: y
 code: (+ x y)

let x 3

slide-32
SLIDE 32

Lexical scoping vs dynamic scoping

  • The alternative to lexical scoping is called dynamic scoping.
  • In dynamic scoping, if a function f references a non-local

variable x, the language will look for x in the function that called f. – If it's not found, will look in the function that called the function that called f (and so on).

  • Contrast with lexical scoping, where the language would look for

x in the scope where f was defined.

Spring 2013 33 Programming Languages

slide-33
SLIDE 33

Why lexical scope?

1. Function meaning does not depend on variable names used Example: Can change body of a function to use q instead of x – Lexical scope: it can’t matter – Dynamic scope: Depends how result is used When the anonymous function that f returns is called, in lexical scoping, we always know where the values of x, y, and z will be (what frames they're in). With dynamic scoping, x and y will be searched for in the functions that called the anonymous function, so who knows where they'll be.

Spring 2013 34 Programming Languages

(define (f y) (let ((x (+ y 1))) (lambda (z) (+ x y z)))

slide-34
SLIDE 34

Why lexical scope?

1. Function meaning does not depend on variable names used Example: Can remove unused variables – Dynamic scope: But maybe some g uses it (weird) – You would never write this in a lexically-scoped language, because the binding of x to 3 is never used.

  • (No way for g to access this particular binding of x.)

– In a dynamically-scoped language, g might refer to a non-local variable x, and this binding might be necessary.

Spring 2013 35 Programming Languages

(define (f g) (let ((x 3)) (g 2)))

slide-35
SLIDE 35

Why lexical scope?

  • 2. Easy to reason about functions where they're defined.

Example: Dynamic scope tries to add a string to a number (b/c in the call to (+ x y), x will be "hello")

Spring 2013 36 Programming Languages

(define x 1) (define (f y) (+ x y)) (define g (let ((x "hello")) (f 4))

slide-36
SLIDE 36

Why lexical scope?

3. Closures can easily store the data they need – Many more examples and idioms to come

  • The anonymous function returned by gteq references a non-

local variable x.

  • In lexical scoping, the closure created for the anonymous

function will point to gteq's frame so x can be found.

  • In dynamic scoping, x would not be found at all.

Spring 2013 37 Programming Languages

(define (gteq x) (lambda (y) (>= y x))) (define (no-negs lst) (filter (gteq 0) lst))

slide-37
SLIDE 37

Does dynamic scope exist?

  • Lexical scope for variables is definitely the right default

– Very common across languages

  • Dynamic scope is occasionally convenient in some situations

– So some languages (e.g., Racket) have special ways to do it – But most don’t bother

  • Historically, dynamic scoping was used more frequently in older

languages because it's easier to implement than lexical scoping. – Strategy: Just search through the call stack until variable is

  • found. No closures needed.

– Call stack maintains list of functions that are currently being called, so might as well use it to find non-local variables.

Spring 2013 38 Programming Languages

slide-38
SLIDE 38

Iterators made better

  • Functions like map and filter are much more powerful thanks

to closures and lexical scope

  • Function passed in can use any “private” data in its environment
  • Iterator (e.g., map or filter) “doesn’t even know the data is there”

– It just calls the function that it's passed, and that function will take care of everything.

Spring 2013 40 Programming Languages

slide-39
SLIDE 39

Review of foldr

foldr (sometimes also called accumulate, reduce, or inject) is another very famous iterator over recursive structures Accumulates an answer by repeatedly applying f to answer so far – (foldr f base (x1 x2 x3 x4)) computes (f x1 (f x2 (f x3 (f x4 base))))

Spring 2013 41 Programming Languages

(define (foldr f base lst) (if (null? lst) base (f (car lst) (foldr f base (cdr lst))))) – This version “folds right”; another version “folds left” – Whether the direction matters depends on f (often not)

slide-40
SLIDE 40

Examples with foldr

These are useful and do not use “private data”

Spring 2013 42 Programming Languages

These are useful and do use “private data” (define (f1 lst) (foldr + 0 lst)) (define (f2 lst) (foldr (lambda (x y) (and (>= x 0) y)) #t lst)) (define (f3 lo hi lst) (foldr (lambda (x y) (+ (if (and (>= x lo) (<= x hi)) 1 0) y)) 0 lst)) (define (f4 g lst) (foldr (lambda (x y) (and (g x) y)) #t lst))