CS450  ☽  Specifying λE  ☽  Lecture 14  ☽  Tiago Cogumbreiro

CS450

Structure of Higher Level Languages

Lecture 14: Specifying λE

Tiago Cogumbreiro

1 / 28

CS450  ☽  Specifying λE  ☽  Lecture 14  ☽  Tiago Cogumbreiro

Lexical Scope

2 / 28

CS450  ☽  Specifying λE  ☽  Lecture 14  ☽  Tiago Cogumbreiro

Lexical Scope

  • Binding: association between a variable and a value.
  • Scope of a binding: the text where occurrences of this name refer to the binding
  • Lexical (or static) scope: the innermost lexically-enclosing construct declaring that variable

Did you know? In Computer Science, static analysis corresponds to analyzing the source code, without running the program.

(define (f)
  (define x 10) ; visible: `f`
  (define y 20) ; visible: `f, f.x`
  (+ x y))      ; visible: `f, f.x, f.y`

; visible: `f`
(define x 1)
; visible: `f, x`
(define y (+ x 1))
; visible: `f, x, y`
(check-equal? (f) 30) ; yields (+ `f.x` `f.y`)
3 / 28

CS450  ☽  Specifying λE  ☽  Lecture 14  ☽  Tiago Cogumbreiro

Dynamic Scope

4 / 28

CS450  ☽  Specifying λE  ☽  Lecture 14  ☽  Tiago Cogumbreiro

Lexical scope vs dynamic scope

  • Lexical scoping is the default in all popular programming languages
  • With lexical scoping, we can analyze the source code to identify the scope of every variable
  • With lexical scoping, the programmer can reason about each function independently

What is a dynamic scope?

  • Variable scope depends on the calling context
  • Renders all variables global

appeared in McCarthy’s Lisp 1.0 as a bug and became a feature in all later implementations, such as MacLisp, Gnu Emacs Lisp.

Moreau, L. Higher-Order and Symbolic Computation (1998) 11: 233. DOI:10.1023/A:1010087314987

*;; NOT VALID RACKET CODE!!!
(define (f) `x`)

(define (g `x`) (f))
(check-equal? (g 10) 10)

(define `x` 20)
(check-equal? (f) 20)
5 / 28

CS450  ☽  Specifying λE  ☽  Lecture 14  ☽  Tiago Cogumbreiro

Example

What is the result of evaluating (g)?

(define x 1)

(define (f y) (+ y x))

(define (g)
  (define x 2)
  (define y 3)
  (f (+ x y)))

*(check-equal? (g) ???)
6 / 28

CS450  ☽  Specifying λE  ☽  Lecture 14  ☽  Tiago Cogumbreiro

Example

What is the result of evaluating (g)?

(define x 1)

(define (f f:y) (+ f:y x))

(define (g)
  (define g:x 2)
  (define g:y 3)
  (f (+ g:x g:y)))

*(check-equal? (g) 6)
7 / 28

CS450  ☽  Specifying λE  ☽  Lecture 14  ☽  Tiago Cogumbreiro

Why lexical scoping?

  • Lexical scoping is important for using functions-as-values
  • To implement our Mini-Racket we will need to implement lexical scoping
8 / 28

CS450  ☽  Specifying λE  ☽  Lecture 14  ☽  Tiago Cogumbreiro

Example

What is the result of evaluating (g)?

(define (g) x)

(define x 10)

*(check-equal? (g) ??)
9 / 28

CS450  ☽  Specifying λE  ☽  Lecture 14  ☽  Tiago Cogumbreiro

Example

What is the result of evaluating (g)?

(define (g) x)
*; (g) throws an error here
(define x 10)

*(check-equal? (g) 10)

We can define a function g that refers to an undefined variable x; variable x must be defined before calling g.

In Racket, variable definition produces a side-effect, as the definition of x impacted a previously defined function g. In Module 5, we implement the semantics of define.

10 / 28

CS450  ☽  Specifying λE  ☽  Lecture 14  ☽  Tiago Cogumbreiro

Accessing variables outside a function

The body of a function can refer to variables defined outside of that function.

It can access variables defined outside of the function, but where exactly?

The function's body can access any variable that is accessible/visible when the function is defined, which is known as the lexical scope.

In the following example, the function returns 3 and not 10, even though variable x is now 10.

; For a given x create a new function that always returns x
(define (getter `x`) `(lambda () x)`)
(define get3 (getter 3)) ; At creation time, x = 3
*(define x 10)
(check-equal? 3 (get3)) ; At call time, x = 10
11 / 28

CS450  ☽  Specifying λE  ☽  Lecture 14  ☽  Tiago Cogumbreiro

Function closures

12 / 28

CS450  ☽  Specifying λE  ☽  Lecture 14  ☽  Tiago Cogumbreiro

Recall that functions capture variables

Function closure

  • A function closure is the return value of function declaration (i.e., the function value)
  • Definition: A function closure is a pair that stores a function declaration and its lexical environment (i.e., the state of each variable captured by the function declaration)
  • The technique of creating a function closure is used by compilers/interpreters to represent function values

Recall that function declaration != function definition:

  • Function declaration: ( lambda ( variable* ) term+)
  • Function definition: ( define (variable+ ) term+ )
13 / 28

CS450  ☽  Specifying λE  ☽  Lecture 14  ☽  Tiago Cogumbreiro

Now we know what a function closure is

  1. How to compute the variables in a closure
  2. When to set the values of each variable in a closure
14 / 28

CS450  ☽  Specifying λE  ☽  Lecture 14  ☽  Tiago Cogumbreiro

Function closures: captured variables

It is crucial for us to know how variables are captured in Racket.

Given an expression the set of free variables can be defined inductively:

  • When the expression is a variable x, the set of free variables is { x }.
  • When the expression is a (lambda (x) e), the set of free variables is that of expression e minus variable x.
  • When the expression is a function application (e1 e2), the set of free variables is the union of the set of free variables of e1 and the set of free variables of e2.

Captured variables: Given an expression (lambda (x) e) a function closure captures the set of free variables of expression (lambda (x) e).

15 / 28

CS450  ☽  Specifying λE  ☽  Lecture 14  ☽  Tiago Cogumbreiro

Captured variables examples

Let us compute $\mathrm{fv}$ (lambda (x) (+ x y)):

  1. The free variables of a $\lambda$ are the free variables of the body of the function minus parameter $x$.

$\mathrm{fv}$ (lambda (x) (+ x y)) = $\mathrm{fv}$ (+ x y) $\setminus \{ x \}$

  1. We are now in a case of function application, which is the union of the free variables of each of its sub-expressions.

$\mathrm{fv}$ (+ x y) $\setminus \{ x \} = \big( \mathrm{fv}(+) \cup \mathrm{fv}(x) \cup \mathrm{fv}(y) \big) \setminus \{ x \}$

  1. Finally, we reach the case where each argument of free-vars is a variable.
$$\big( \mathrm{fv}(+) \cup \mathrm{fv}(x) \cup \mathrm{fv}(y) \big) \setminus \{ x \} = \big(\{+\} \cup \{x\} \cup \{y\}\big) \setminus \{x\} = \{+, x, y\} \setminus \{x\} = \color{green} \{+, y\} $$
16 / 28

CS450  ☽  Specifying λE  ☽  Lecture 14  ☽  Tiago Cogumbreiro

What creates an environment?

Definition: At any execution point there is an environment, which maps each variable to a value.

What creates environments:

  • Each branch inside a cond creates an environment
  • The body of a function creates an environment

What updates an environment:

  • The arguments of a lambda are added to the function's body environment
  • A (define x e) updates the current environment by adding/updating variable x and setting it to the value that results from evaluating e
17 / 28

CS450  ☽  Specifying λE  ☽  Lecture 14  ☽  Tiago Cogumbreiro

Example 1: capture an argument

The lambda is capturing x as the parameter of getter at creation time, so when we call (get3) we get (lambda () 3).

(define (getter `x`)
  (lambda () x)) ; `getter:x`

(define get3 (getter 3)) ; `getter:x = 3; (lambda () getter:x)`
(check-equal? 3 (get3))
18 / 28

CS450  ☽  Specifying λE  ☽  Lecture 14  ☽  Tiago Cogumbreiro

Example 2: cond starts a new scope

Function getter captured x at the outermost scope (the x captured at function declaration time). Inside the branches of cond we have a new scope, which means that getter is unaffected by the redefinition of x.

(define (getter) `x`) ; `root.x`
(define x 10)         ; root.x = 10
; Each branch of the cond creates a new environment
; so it does not affect getter
(cond [#t (define x 20) (check-equal? 10 (getter))])
(check-equal? 10 (getter))
19 / 28

CS450  ☽  Specifying λE  ☽  Lecture 14  ☽  Tiago Cogumbreiro

Example 3: define shadows parameters

Function getter returns variable x from the environment of function f. When calling f 20 the last value of variable x in the scope of f is 10, due to (define x 10), which overwrites the function's parameter x=20.

(define (f x)
  (define (getter) `x`) ; `f.x = ?`
  (define x 10)       ; `f.x = 10`
  getter)

(define g (f 20))
(check-equal? 10 (g))
20 / 28

CS450  ☽  Specifying λE  ☽  Lecture 14  ☽  Tiago Cogumbreiro

Language $\lambda_S$ is slow

21 / 28

CS450  ☽  Specifying λE  ☽  Lecture 14  ☽  Tiago Cogumbreiro

Recall $\lambda_S$

Syntax

$$e ::= v \mid x \mid (e_1\ e_2) \qquad v ::= n \mid \lambda x.e $$

Semantics

$$v \Downarrow v\ \texttt{(E-val)} $$

$$\frac{ {\color{green} e_f} \Downarrow {\color{purple}\lambda x}.{\color{red} e_b} \qquad {\color{blue}e_a} \Downarrow {\color{blue}v_a} \qquad {\color{red}e_b} \overbrace{[{\color{purple}x} \mapsto {\color{blue}v_a}]}^{\color{brown}\text{Complexity?}} \Downarrow {\color{red}v_b} }{ ({\color{green}e_f}\ {\color{blue}e_a}) \Downarrow \color{red}v_b } \ \texttt{(E-app)} $$
22 / 28

CS450  ☽  Specifying λE  ☽  Lecture 14  ☽  Tiago Cogumbreiro

A complexity analysis on function-call

Let us consider our implementation of Mini-Racket, and draw our attention to function substitution.

Given a function call $(e_f\ e_a)$

  1. We evaluate $e_f$ down to a function $(\lambda (x)\ e_b)$
  2. We evaluate $e_a$ down to a value $v_a$
  3. We evaluate $e_b[x \mapsto v_a]$ down to a value $v_b$

What is the complexity of the substitution operation $[x \mapsto v_a]$?

23 / 28

CS450  ☽  Specifying λE  ☽  Lecture 14  ☽  Tiago Cogumbreiro

A complexity analysis on function-call

Let us consider our implementation of Mini-Racket, and draw our attention to function substitution.

Given a function call $(e_f\ e_a)$

  1. We evaluate $e_f$ down to a function $(\lambda (x)\ e_b)$
  2. We evaluate $e_a$ down to a value $v_a$
  3. We evaluate $e_b[x \mapsto v_a]$ down to a value $v_b$

What is the complexity of the substitution operation $[x \mapsto v_a]$?

The run-time grows linearly on the size of the expression, as we must replace $x$ by $v_a$ in every sub-expression of $e_b$.

23 / 28

CS450  ☽  Specifying λE  ☽  Lecture 14  ☽  Tiago Cogumbreiro

Can we do better?

24 / 28

CS450  ☽  Specifying λE  ☽  Lecture 14  ☽  Tiago Cogumbreiro

Can we do better?

Yes, we can sacrifice some space

to improve the run-time speed.

24 / 28

CS450  ☽  Specifying λE  ☽  Lecture 14  ☽  Tiago Cogumbreiro

Decreasing the run time of substitution

Idea 1: Use a lookup-table to bookkeep the variable bindings

Idea 2: Introduce closures/environments

25 / 28

CS450  ☽  Specifying λE  ☽  Lecture 14  ☽  Tiago Cogumbreiro

$\lambda_E$-calculus: $\lambda$-calculus with environments

We introduce the evaluation of expressions down to values, parameterized by environments:

$$ {\color{blue}e} \Downarrow_{\color{blue}E} {\color{green}v} $$

The evaluation takes two arguments: an expression $e$, and an environment $E$. The evaluation returns a value $v$.

Attention!

Homework Assignment 4:

  • Evaluation ${\color{blue}e} \Downarrow_{\color{blue}E} {\color{green}v}$ is implemented as function (e:eval env exp) that returns a value e:value, where environment env is a hash, and expression exp is an e:expression.
  • functions and structs prefixed with s: correspond to the $\lambda_S$ language (Section 1).
  • functions and structs prefixed with e: correspond to the $\lambda_E$ language (Section 2)
26 / 28

CS450  ☽  Specifying λE  ☽  Lecture 14  ☽  Tiago Cogumbreiro

$\lambda_E$-calculus: $\lambda$-calculus with environments

Syntax

$$e ::= v \mid x \mid (e_1\ e_2) \mid {\color{purple}\lambda x.e} \qquad v ::= n \mid {\color{purple} \{E, \lambda x.e\}} $$

Semantics

$$v \Downarrow_{\color{blue}E} v \qquad (\texttt{E-val}) $$ $$x \Downarrow_{\color{blue}E} {\color{blue}E}(x) \qquad (\texttt{E-var}) $$ $$\lambda x.e \Downarrow_{\color{blue}E} \{{\color{blue}E},\lambda x.e\} \qquad (\texttt{E-clos}) $$ $$\frac{ e_f \Downarrow_{\color{blue}E} \{{\color{green}E_b},\lambda x. e_b\} \qquad e_a \Downarrow_{\color{blue}E} v_a \qquad e_b \Downarrow_{\mathbf{\color{green}E_b[x\mapsto v_a]}} v_b }{ (e_f\ e_a) \Downarrow_{\color{blue}E} v_b } \qquad \texttt{(E-app)} $$
27 / 28

CS450  ☽  Specifying λE  ☽  Lecture 14  ☽  Tiago Cogumbreiro

Overview of $\lambda_E$-calculus

Notable differences

  1. Declaring a function is an expression that yields a function value (a closure), which packs the environment at creation-time with the original function declaration.
  2. Calling a function unpacks the environment $E_b$ from the closure and extends environment $E_b$ with a binding of parameter $x$ and the value $v_a$ being passed

Environments

An environment $E$ maps variable bindings to values.

Constructors

  • Notation $\emptyset$ represents the empty environment (with zero variable bindings)
  • Notation $E[x \mapsto v]$ extends an environment with a new binding (overwriting any previous binding of variable $x$).

Accessors

  • Notation $E(x) = v$ looks up value $v$ of variable $x$ in environment $E$
28 / 28
Rotate for a larger view