Recitation 11 -- Variable Scoping and Mutable Data
Skills: 7, 8
Objectives
- Understand the program directory mental model for variable scoping
- Compare Python and Pyret approaches to variable declaration and mutation
- Work with mutable data structures and understand aliasing
- Distinguish between structural and reference equality
I. Introduction (5 minutes)
The "program directory" is our mental model of how a language keeps track of variable names and their associated values. Both Python and Pyret use this concept, but with key differences.
Key Differences:
- Pyret: Must choose mutable (
var x = 5
) vs immutable (x = 5
) at declaration - Python: Assignment
=
both creates and updates variables
II. Python Variable Scoping (15 minutes)
A. Global vs Local Bindings
Example 1: Global Assignment
score = 100 # score is bound in the global directory
def show_score():
return score
print(show_score()) # prints 100
The function show_score
looks up score
in the global directory.
Example 2: Local Binding and Shadowing
score = 100
def change_score():
score = 50 # creates NEW local variable, doesn't change global
return score
print(change_score()) # prints 50
print(score) # prints 100 -- global unchanged
B. Using Global and Nonlocal
Example 3: Actually Updating Global
score = 100
def update_global_score():
global score # explicitly refer to global variable
score = 75
update_global_score()
print(score) # prints 75
Example 4: Nested Functions
def outer():
count = 10
def inner():
nonlocal count # refer to count from outer function
count = count + 5
inner()
return count
print(outer()) # prints 15
III. Pyret Variable Scoping (15 minutes)
A. Immutable vs Mutable Declarations
Example 1: Immutable Declaration
x = 10
# x is immutable - this would cause an error:
# x := 20
Example 2: Mutable Declaration
var x = 10
x := x + 5 # x becomes 15
x # returns 15
Check Understanding: Ask, "What's the difference between =
and :=
in Pyret?"
B. No Accidental Shadowing
In Pyret, once a name is declared in a scope, re-declaring that same name is forbidden. This prevents accidental shadowing.
Example:
y = 20
# This would cause an error in Pyret:
# y = 30 (can't re-declare y)
# But this works:
var z = 20
z := 30 # explicit update
C. Program Directory Comparison
Discussion using Program Directory Model:
Pyret:
var x = 10
recordsx
as mutable in directoryx := 20
directly changes that entryx = 50
after declaration causes error (name already exists)
Python:
x = 10
at global level addsx
to global directory- Inside function,
x = 50
creates new local entry - Global
x
unchanged unless usingglobal x
IV. Memory Model (5 minutes)
We expand our program directory model to include a "heap" -- where mutable structured data lives.
Mental Model:
- Program Directory: Maps names to values (or addresses for structured data)
- Heap: Stores structured data objects with unique addresses
Example:
- Directory:
book1 -> @1001
- Heap:
@1001: LibraryBook("Python Guide", 3)
V. Student Record Example (10 minutes)
A. Data Design
Each student has an ID, name, and current credit hours. We want to update credits when they register for courses.
B. Python Version
from dataclasses import dataclass
@dataclass
class StudentRecord:
id: int
name: str
credits: int
student1 = StudentRecord(12345, "Alice Smith", 15)
student1.credits = student1.credits + 3 # registered for 3-credit course
print(student1.credits) # prints 18
Python doesn't require special syntax -- all fields are mutable by default.
C. Pyret Version
data StudentRecord:
student-record(id :: Number,
name :: String,
ref credits :: Number) # ref marks as mutable
end
student1 = student-record(12345, "Alice Smith", 15)
# Access mutable field with !
student1!credits # returns current credits
# Update credits using special syntax
student1!{credits: student1!credits + 3}
student1!credits # now returns 18
Key Differences:
- Mutable fields marked with
ref
- Access with
!
instead of.
- Update with
!{field: new-value}
syntax
VI. Aliasing and the Heap (15 minutes)
Aliasing occurs when two names refer to the same object (same heap address).
Python Example:
student1 = StudentRecord(12345, "Alice Smith", 15)
student2 = student1 # aliasing -- both refer to same object
student1.credits = student1.credits + 3
print(student2.credits) # prints 18
Pyret Example:
student1 = student-record(12345, "Alice Smith", 15)
student2 = student1 # aliasing
student1!{credits: student1!credits + 3}
student2!credits # returns 18
Program Directory/Heap Visualization:
Directory:
student1 → @1001
student2 → @1001 # Same address
Heap:
@1001: StudentRecord(12345, "Alice Smith", 18)
VII. Equality: Structural vs Reference (10 minutes)
A. Two Types of Equality
Structural Equality: Same contents in all fields Reference Equality: Same object in memory (same heap address)
B. Examples with Student Records
alice1 = StudentRecord(100, "Alice", 15)
alice2 = StudentRecord(100, "Alice", 15) # same content, different object
alice3 = alice1 # alias
# Structural equality (==)
print(alice1 == alice2) # True -- same contents
print(alice1 == alice3) # True -- same contents
# Reference equality (is)
print(alice1 is alice2) # False -- different objects
print(alice1 is alice3) # True -- same object (alias)
C. Mutation Effects on Equality
alice1.credits = 18 # update through one alias
print(alice1 == alice2) # now False -- different contents
print(alice1 is alice3) # still True -- same object
print(alice1.credits == alice3.credits) # True -- both show 18
VIII. Recap and Wrap-Up (5 minutes)
Key Points Discussion:
- Program Directory: Mental model for tracking variable names and values
- Python Scoping:
=
both declares and updates; useglobal
/nonlocal
for outer scope access - Pyret Scoping: Explicit mutable (
var
) vs immutable declarations; no accidental shadowing - Mutable Data: Pyret requires
ref
marking; Python makes all fields mutable - Aliasing: Multiple names can refer to same object; changes affect all aliases
- Equality: Structural (
==
) vs reference (is
) equality in Python
Reflection Questions:
- "Why is understanding aliasing important when working with mutable data?"