Skip to main content

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 records x as mutable in directory
  • x := 20 directly changes that entry
  • x = 50 after declaration causes error (name already exists)

Python:

  • x = 10 at global level adds x to global directory
  • Inside function, x = 50 creates new local entry
  • Global x unchanged unless using global 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; use global/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?"