# Scoping: Geometric Scene Evolution as Programming Pedagogy This document articulates ggblab's **foundational educational mission**: use geometric scene construction to teach **variable scoping** and **computational thinking**—concepts often neglected or poorly understood in Python education. --- ## Part 1: Pedagogical Foundation ### The Problem: Python's Scoping Crisis in Education #### Why Python Fails at Teaching Scoping Python's dynamic nature and permissive scoping rules make it a **poor vehicle for teaching fundamental programming concepts**: | Issue | Python Behavior | Educational Consequence | |-------|----------------|------------------------| | **Implicit global access** | Variables can be read from outer scopes without declaration | Students don't understand scope boundaries | | **Late binding** | Closures capture variable *references*, not values | Confusing behavior in loops (`lambda i: i` problem) | | **Weak encapsulation** | No private variables; convention-based (`_var`) | Students don't internalize information hiding | | **Namespace pollution** | `from module import *` is legal | No sense of explicit dependencies | | **Mutable default arguments** | `def f(x=[]):` creates shared state | Violates expectations of function isolation | **Result**: Students write code that "works" but have **no mental model** of: - Where variables are defined (scope boundaries) - What depends on what (dependency relationships) - When values are captured vs. referenced (binding semantics) - How to reason about side effects #### Traditional Approaches Fall Short **Textbook explanations**: Abstract diagrams of "local," "enclosing," "global," "built-in" scopes (LEGB rule) - ❌ No concrete, visual anchor - ❌ Disconnected from real problem-solving - ❌ Students memorize without understanding **Toy examples**: `x = 1` at global level, `x = 2` inside function - ❌ Artificial; no meaningful context - ❌ Doesn't generalize to complex programs - ❌ Boring; students disengage **What's missing**: A **domain that students already care about** (mathematics) where scoping naturally emerges. --- ### The Solution: Geometric Scene Construction as Scoping Model #### Why Geometry Works **Mathematical constructions are inherently scoped**: ``` Given: - Points A, B [Base scope: axioms/givens] Construct: - Line AB [Scope 1: depends on A, B] - Midpoint M of AB [Scope 2: depends on AB, hence on A, B] - Perpendicular bisector L of AB [Scope 2: depends on AB] - Point C on L [Scope 3: depends on L, hence on AB, A, B] - Triangle ABC [Scope 4: depends on A, B, C] Property to prove: - Triangle ABC is isosceles [Verification scope: accesses A, B, C, M, L] ``` **This is a scoping hierarchy**: - **Base scope** (Points A, B): Foundational definitions; no dependencies - **Scope 1** (Line AB): Depends on base scope; closes over A, B - **Scope 2** (Midpoint M, Bisector L): Depends on Scope 1; transitively depends on base - **Scope 3** (Point C): Depends on Scope 2; long dependency chain - **Verification scope**: Reads from multiple scopes; proves emergent properties #### The Isomorphism: Geometry ↔ Programming | Geometric Concept | Programming Concept | ggblab Manifestation | |------------------|---------------------|---------------------| | **Base points (A, B)** | Function parameters / global constants | User-defined initial state | | **Constructed line (AB)** | Local variable depending on parameters | `Line(A, B)` object in dependency graph | | **Derived point (M = midpoint)** | Computed value from local variables | `Midpoint(A, B)` in SymPy/GeoGebra | | **Constraint (C on L)** | Variable binding within scope | Point's position determined by ancestor objects | | **Property verification** | Function accessing multiple scopes | SymPy `verify_property()` traverses graph | | **Construction sequence** | Call stack / execution order | Topological sort of dependency graph | | **Parameter sweep (vary A)** | Closure capturing mutable reference | Scene Timeline records scope evolution | #### Visual Manifestation in GeoGebra **GeoGebra's construction protocol IS a scope tree**: ``` Construction Protocol: 1. A = (1, 2) → Scope: Global (user-defined) 2. B = (4, 6) → Scope: Global (user-defined) 3. AB = Line[A, B] → Scope: Local to AB; depends on {A, B} 4. M = Midpoint[A, B] → Scope: Local to M; depends on {A, B} 5. L = PerpendicularLine[M, AB] → Scope: Local to L; depends on {M, AB} → {A, B} 6. C = Point[L] → Scope: Local to C; depends on {L} → {M, AB} → {A, B} ``` **Dependency graph** (generated by ggblab's `parse()` method): ``` A ──┐ ├──> AB ──> L ──> C B ──┘ └──> M ──┘ ``` **This IS a scope tree**: - Root nodes (A, B): No dependencies → global scope - Internal nodes (AB, M, L): Depend on ancestors → nested scopes - Leaf nodes (C): Depend on long chains → deeply nested scopes **Students see the scoping structure visually**. No abstract diagrams needed. --- ### Computational Thinking via Geometric Construction #### The Four Pillars **Computational Thinking (Wing, 2006)** comprises: 1. **Decomposition**: Break complex problems into manageable parts 2. **Pattern Recognition**: Identify similarities and recurring structures 3. **Abstraction**: Focus on essential information; hide irrelevant details 4. **Algorithm Design**: Create step-by-step solutions **Geometric construction naturally teaches all four**: #### Decomposition **Geometry**: "Construct an equilateral triangle" - Step 1: Define base segment AB - Step 2: Construct circle centered at A with radius |AB| - Step 3: Construct circle centered at B with radius |AB| - Step 4: Find intersection C of the two circles - Step 5: Triangle ABC is equilateral **Programming**: "Write a function to compute factorial" - Step 1: Base case (n = 0) - Step 2: Recursive case (n * factorial(n-1)) - Step 3: Combine **Skill transfer**: Both require breaking monolithic tasks into ordered subtasks. #### Pattern Recognition **Geometry**: Recognize that "perpendicular bisector + midpoint" pattern appears in many constructions - Isosceles triangle construction - Square construction - Circle-tangent constructions **Programming**: Recognize that "initialize accumulator + iterate + update" pattern appears in many algorithms - Summing a list - Finding maximum - Filtering data **Skill transfer**: Both require identifying reusable templates. #### Abstraction **Geometry**: - Abstract away specific coordinates: "Triangle ABC" (not "Triangle at (0,0), (1,0), (0.5, √3/2)") - Focus on relationships: "Perpendicular" (not "slope = -1/m") **Programming**: - Abstract away implementation: `sort(list)` (not manual quicksort) - Focus on interfaces: "This function takes a list and returns a sorted list" **Skill transfer**: Both require ignoring low-level details to reason at higher levels. #### Algorithm Design **Geometry**: "Given three non-collinear points, construct the circumcircle" - Algorithm 1: Perpendicular bisectors of two sides intersect at circumcenter - Algorithm 2: Use formula for circumcenter coordinates - Trade-offs: Geometric vs. algebraic; visual clarity vs. computational efficiency **Programming**: "Sort a list of numbers" - Algorithm 1: Bubble sort (simple, slow) - Algorithm 2: Merge sort (complex, fast) - Trade-offs: Simplicity vs. efficiency **Skill transfer**: Both require designing step-by-step procedures and evaluating trade-offs. --- ### Scoping Pedagogy: From Math to Code #### Concrete Example: Isosceles Triangle Construction **Mathematical Statement**: > Construct an isosceles triangle ABC where AB is the base. **Geometric Construction** (GeoGebra): ``` Step 1: Define base segment A = Point(0, 0) # Scope: Global (parameter) B = Point(4, 0) # Scope: Global (parameter) Step 2: Construct perpendicular bisector M = Midpoint(A, B) # Scope: Nested; depends on {A, B} AB = Segment(A, B) # Scope: Nested; depends on {A, B} L = PerpendicularLine(M, AB) # Scope: Nested; depends on {M, AB} Step 3: Choose point on bisector C = Point(L) # Scope: Nested; depends on {L} # Transitively depends on {M, AB, A, B} Result: Triangle(A, B, C) is isosceles (AC = BC) ``` **Dependency Graph** (ggblab `parse()` output): ``` A ──┬──> M ──┐ │ ├──> L ──> C └──> AB ──┘ B ──┘ ``` **Translation to Python Scoping**: ```python # Global scope: Parameters (like A, B in geometry) def construct_isosceles_triangle(base_length=4): # Scope 1: Define base points (like A, B) A = Point(0, 0) B = Point(base_length, 0) # Scope 2: Derived objects (like M, AB, L) def perpendicular_bisector(): # This function "closes over" A and B # (Like M and L depend on A and B) midpoint_x = (A.x + B.x) / 2 midpoint_y = (A.y + B.y) / 2 M = Point(midpoint_x, midpoint_y) # Perpendicular direction dx = B.x - A.x dy = B.y - A.y perp_dx, perp_dy = -dy, dx # Rotate 90° return M, (perp_dx, perp_dy) # Scope 3: Choose point on bisector (like C on L) M, perp_dir = perpendicular_bisector() height = 3 # User-chosen parameter C = Point(M.x + height * perp_dir[0], M.y + height * perp_dir[1]) # Return: Triangle closure # (Like Triangle ABC in geometry) return Triangle(A, B, C) # Verification: Access to outer scopes triangle = construct_isosceles_triangle(base_length=4) assert triangle.is_isosceles() # Reads A, B, C from nested scopes ``` **What students learn**: 1. **Scope boundaries**: Variables defined in `perpendicular_bisector()` are not accessible outside 2. **Closure semantics**: Inner function captures `A` and `B` from outer scope 3. **Dependency chains**: `C` depends on `M`, which depends on `A` and `B` 4. **Verification scope**: `is_isosceles()` can read from multiple construction scopes **Key insight**: The geometric construction makes scoping **tangible and visual**, not abstract. ### Parameter Sweeps and Scope Evolution **Geometric Scenario**: Vary the height of point C along the perpendicular bisector. **Scene Timeline** (ggblab v0.8): ```python # Define parametric construction def construct_with_height(h): A = Point(0, 0) B = Point(4, 0) M = Midpoint(A, B) C = Point(M.x, M.y + h) # Height parameter return {'A': A, 'B': B, 'C': C, 'triangle': Triangle(A, B, C)} # Sweep height parameter timeline = SceneTimeline() for h in np.linspace(0, 5, 20): scene = construct_with_height(h) timeline.capture(scene, metadata={'height': h}) # Playback: Watch C move along bisector timeline.play() ``` **Scoping lesson**: - **Parameter binding**: `h` is a parameter; each scene snapshot captures a different binding - **Closure behavior**: At each `h`, the construction "closes over" that specific value - **Scope lifetime**: Old snapshots preserve their scope (like call stack frames) **Programming analogy**: Factory functions and closures ```python def make_multiplier(n): # n is captured in closure def multiply(x): return x * n # Accesses n from outer scope return multiply times_2 = make_multiplier(2) # Captures n=2 times_3 = make_multiplier(3) # Captures n=3 # Each closure has its own scope print(times_2(5)) # 10 (uses n=2) print(times_3(5)) # 15 (uses n=3) ``` **Geometric equivalent**: Each scene snapshot is a closure that captured specific parameter values. **Students see**: Scope is not just static structure; it evolves with program execution. --- ## Part 2: Implementation ### Dependency Graph as Scope Tree **ggblab's `parse()` method** (in `ggblab/parser.py`) constructs a directed graph: ```python def parse(self, protocol: str) -> nx.DiGraph: """ Parse GeoGebra construction protocol into dependency graph. Returns: NetworkX DiGraph where: - Nodes: GeoGebra objects (points, lines, circles, etc.) - Edges: (A -> B) means "B depends on A" """ # ... parse XML ... # Build graph G = nx.DiGraph() for obj in construction_objects: G.add_node(obj['name'], **obj['properties']) for dependency in obj['depends_on']: G.add_edge(dependency, obj['name']) return G ``` **This graph IS the scope tree**: - **Root nodes** (`in_degree == 0`): Global scope / parameters - **Leaf nodes** (`out_degree == 0`): Terminal constructions (no dependents) - **Paths**: Scope nesting depth (path length from root to node) **Educational workflow**: ```python # Student constructs in GeoGebra # Export to ggblab from ggblab import GeoGebraApp ggb = GeoGebraApp() # Parse dependency graph graph = ggb.parse(ggb.get_construction_protocol()) # Visualize scope tree import matplotlib.pyplot as plt import networkx as nx pos = nx.spring_layout(graph) nx.draw(graph, pos, with_labels=True, node_color='lightblue', node_size=500, font_size=10, arrows=True) plt.title("Dependency Graph = Scope Tree") plt.show() # Identify scope levels (topological sort) scope_levels = list(nx.topological_generations(graph)) for i, level in enumerate(scope_levels): print(f"Scope Level {i}: {level}") # Output: # Scope Level 0: ['A', 'B'] # Global scope # Scope Level 1: ['AB', 'M'] # Depends on Level 0 # Scope Level 2: ['L'] # Depends on Level 1 # Scope Level 3: ['C'] # Depends on Level 2 ``` **Students see**: The graph structure reveals scoping hierarchy. ### SymPy Integration and Symbolic Scoping **SymPy objects have explicit dependencies** (unlike Python variables): ```python from sympy import symbols from sympy.geometry import Point, Line, Circle # Symbolic parameters (global scope) a, b = symbols('a b') # Scope 1: Parameterized points A = Point(0, 0) B = Point(a, b) # Scope 2: Derived objects AB = Line(A, B) M = Point(a/2, b/2) # Midpoint formula # Scope 3: Constrained objects # C is on perpendicular through M # Symbolic expression for C's coordinates from sympy import sqrt, solve, Eq # C is distance d from M along perpendicular d = symbols('d', positive=True) slope_AB = b / a # Slope of AB slope_perp = -a / b # Perpendicular slope C_x = M.x + d / sqrt(1 + slope_perp**2) C_y = M.y + d * slope_perp / sqrt(1 + slope_perp**2) C = Point(C_x, C_y) print(f"C depends on: {C.free_symbols}") # Output: {a, b, d} ← Explicit scope dependencies! ``` **Key teaching moment**: - **SymPy tracks scope automatically** via `free_symbols` - **Students see dependencies explicitly**: "C depends on a, b, d" - **No guessing about closures**: Symbolic computation makes scoping transparent **ggblab integration**: ```python # Convert GeoGebra construction to SymPy from ggblab.sympy_bridge import geogrebra_to_sympy sympy_objects = geogrebra_to_sympy(ggb.get_base64()) # Inspect scope dependencies for name, obj in sympy_objects['objects'].items(): if hasattr(obj, 'free_symbols'): deps = obj.free_symbols print(f"{name} depends on: {deps}") # Output: # A depends on: set() # No dependencies (global scope) # B depends on: set() # No dependencies (global scope) # AB depends on: set() # Depends on A, B (already concrete) # M depends on: set() # Depends on A, B (already concrete) # C depends on: {t} # Depends on parameter t ``` **Students learn**: Scoping isn't magic; it's explicit dependency tracking. --- ## Classroom Integration ### Lesson Progression **Lesson 1: Introduction to Scope via Points and Lines** - Goal: Understand that some objects depend on others - Activity: 1. Construct two points A, B in GeoGebra 2. Construct line AB 3. Try to delete A → GeoGebra warns "Line AB depends on A" 4. Export to ggblab; visualize dependency graph - Learning Outcome: "Dependencies are like scope; child objects need parent objects" **Lesson 2: Nested Scopes via Construction Chains** - Goal: Understand transitive dependencies - Activity: 1. Construct: A → B → AB → M (midpoint) → L (perpendicular) → C (point on L) 2. Export to ggblab; compute scope levels (topological sort) 3. Compare to Python nested functions - Learning Outcome: "Scope depth = dependency chain length" **Lesson 3: Closures via Parameter Sweeps** - Goal: Understand closure semantics and parameter binding - Activity: 1. Define slider `h` in GeoGebra (height parameter) 2. Construct triangle with vertex at height `h` 3. Sweep `h` using ggblab Scene Timeline 4. Compare to Python closure capturing variable - Learning Outcome: "Each snapshot captures specific parameter values (like closures)" **Lesson 4: Verification Scope via Symbolic Proofs** - Goal: Understand that verification can access multiple scopes - Activity: 1. Construct isosceles triangle (as above) 2. Use ggblab + SymPy to verify `AC == BC` symbolically 3. Note that verification reads from multiple construction scopes 4. Compare to Python function accessing outer scopes - Learning Outcome: "Verification scope can read (but not modify) construction scopes" ### Assessment Rubric **Objective**: Can students transfer geometric scoping to programming scoping? | Criterion | Novice | Intermediate | Advanced | |----------|--------|--------------|----------| | **Identify dependencies** | Cannot identify which objects depend on which | Can identify direct dependencies (A → B) | Can trace transitive dependencies (A → B → C) | | **Scope boundaries** | Doesn't understand why deleting A breaks AB | Understands parent-child relationships | Can predict cascading effects of modifications | | **Closure behavior** | Cannot explain parameter binding | Understands that snapshots capture parameters | Can implement closures in Python using geometric intuition | | **Transfer to code** | Cannot relate geometry to programming | Can see similarities but not apply | Can write scoped Python code using geometric templates | ### Example Exercise **Geometric Problem**: > Given points A, B, C, construct the incenter I of triangle ABC. **Construction Steps**: 1. Define A, B, C (global scope) 2. Construct angle bisectors (nested scope; depend on A, B, C) 3. Find intersection I of bisectors (nested scope; depends on bisectors) **Programming Challenge**: > Translate the construction to Python code with proper scoping. **Expected Solution**: ```python def construct_incenter(A, B, C): """ Scope hierarchy: - Parameters: A, B, C (like geometric givens) - Nested: angle bisectors (depend on A, B, C) - Nested: incenter I (depends on bisectors) """ # Scope 1: Angle bisectors def angle_bisector(vertex, side1, side2): # Closure over vertex, side1, side2 # ... compute bisector direction ... return bisector_line # Scope 2: Compute bisectors bisector_A = angle_bisector(A, B, C) bisector_B = angle_bisector(B, A, C) # Scope 3: Intersection = incenter I = intersect(bisector_A, bisector_B) return I # Verification scope: Check that I is equidistant from all sides incenter = construct_incenter(A, B, C) assert distance(incenter, side_AB) == distance(incenter, side_BC) ``` **Grading Criteria**: - ✅ Correct scope nesting (inner functions don't leak) - ✅ Closure semantics (bisectors capture vertex coordinates) - ✅ Dependency clarity (I depends on bisectors, which depend on A, B, C) --- ## Why This Matters: The Meta-Goal ### Computational Thinking Is Transferable **The educational research consensus** (Barr & Stephenson, 2011; Brennan & Resnick, 2012): - Computational thinking skills transfer across domains - Abstract concepts need concrete anchors - **Mathematics is the ideal anchor** for programming concepts **ggblab bridges the gap**: ``` Mathematics (already understood) ↓ Geometric construction Dependency relationships (visual, tangible) ↓ ggblab translation Programming scoping (abstract, conceptual) ``` **Traditional CS education fails** because it starts with abstract scoping rules. **ggblab succeeds** because it starts with concrete geometric relationships. ### The Pipeline: From Geometry to Professional Programming **Stage 1: Geometric Exploration (K-8)** - Students learn geometric constructions in GeoGebra - No programming; pure mathematics **Stage 2: Dependency Awareness (Grades 9-10)** - Students export constructions to ggblab - Visualize dependency graphs - Understand "this depends on that" **Stage 3: Scoping Translation (Grades 11-12 / College)** - Students write Python code mirroring geometric constructions - Explicit teaching: "Geometric dependency = programming scope" - Exercises: Translate constructions to code **Stage 4: Professional Programming (College / Career)** - Students write complex software with proper scoping - Intuition: "Check the dependency graph; avoid circular references" - Debugging: "Trace the scope chain; where was this variable defined?" **ggblab is the missing link** between Stages 2 and 3. ### Addressing Python's Weaknesses **Python's educational value is undeniable** (simple syntax, rich libraries). **But its scoping weaknesses are real**. **ggblab compensates** by: 1. **Externalizing scope**: Make dependencies visual (graph), not implicit 2. **Symbolic tracking**: SymPy's `free_symbols` makes scope explicit 3. **Geometric grounding**: Anchor abstract scoping in tangible construction **Result**: Students learn **scoping principles** (not just Python's LEGB rule) that transfer to any language (JavaScript, Java, Rust, etc.). --- ## Implementation Roadmap ### v0.8: Basic Scoping Visualization - [x] Dependency graph construction (`parse()` method) - [ ] Graph visualization with scope-level highlighting - [ ] Example notebook: "Geometric Dependencies as Scope Trees" ### v0.9: Symbolic Scoping - [ ] SymPy integration (`geogrebra_to_sympy()`) - [ ] Automatic scope analysis (track `free_symbols`) - [ ] Example notebook: "Symbolic Dependencies and Closure Semantics" ### v1.0: Parameter Sweeps and Scope Evolution - [ ] Scene Timeline with parameter binding visualization - [ ] Side-by-side: geometric scene + scope tree + Python closure - [ ] Example notebook: "Closures via Geometric Parameter Sweeps" ### v1.1: Educational Lesson Modules - [ ] Lesson 1: Points and Lines (Introduction to Dependencies) - [ ] Lesson 2: Construction Chains (Transitive Dependencies) - [ ] Lesson 3: Parameter Sweeps (Closures) - [ ] Lesson 4: Verification (Multi-Scope Access) ### v1.2: Assessment and Feedback - [ ] Automated exercises: "Translate this construction to code" - [ ] Rubric-based grading (dependency identification, scope boundaries, etc.) - [ ] Instructor dashboard: Track student understanding of scoping --- ## Success Metrics ### Short-Term (v1.0) - [ ] Students can identify dependencies in geometric constructions (100% accuracy on simple graphs) - [ ] Students can map geometric dependencies to Python scopes (75% accuracy on translation exercises) - [ ] Students report: "I finally understand closures" (qualitative feedback) ### Medium-Term (v1.2) - [ ] Adoption by 1+ institution (geometry + CS course pairing) - [ ] Instructor feedback: "Students debug scope errors faster after using ggblab" - [ ] Comparative study: ggblab users vs. control group on scoping quiz (statistically significant improvement) ### Long-Term (v2.0+) - [ ] Scoping pedagogy documented in CS education literature - [ ] ggblab referenced in computational thinking curricula - [ ] Transfer studies: Do ggblab users write better-scoped code in other languages? (JavaScript, Java, etc.) --- ## Related Work ### Computational Thinking Frameworks - **Wing (2006)**: "Computational Thinking" - abstraction and automation - **Brennan & Resnick (2012)**: CT assessment in Scratch (concepts, practices, perspectives) - **Barr & Stephenson (2011)**: CT in K-12 - data, algorithms, abstraction **ggblab contribution**: Adds **scoping** to the CT framework via geometric grounding. ### Visual Programming and Scoping - **Scratch**: Block-based; scopes are implicit (global vs. sprite-local) - **Alice**: 3D animations; method scope visible but not emphasized - **Blockly**: Code blocks; scoping taught via block nesting **ggblab advantage**: Uses **real mathematics** (not contrived examples) and bridges to **text-based code** (Python). ### Math + CS Integration - **Bootstrap**: Algebra via functional programming (Racket/Pyret) - **ggblab**: Geometry via dependency graphs and scoping **Complementary**: Bootstrap teaches functions; ggblab teaches scope and dependencies. --- ## Part 3: Philosophical Perspective ### Philosophical Stance: Mathematics First #### Why Not Teach Scoping Directly in Python? **Standard approach**: Start with `def`, `global`, `nonlocal`, LEGB rule - ❌ Students memorize rules without mental models - ❌ No intuition for why scoping matters - ❌ Debugging scope errors is trial-and-error **ggblab approach**: Start with geometric dependencies (already intuitive) - ✅ Students already understand "line depends on points" - ✅ Natural mental model: construction order = scope hierarchy - ✅ Debugging geometric constructions trains dependency reasoning **Principle**: **Teach concepts first in familiar domain (math), then transfer to code**. #### The Cognitive Science Rationale **Dual Coding Theory** (Paivio, 1986): - Combining verbal (code) and visual (geometry) representations enhances learning - Students who see scoping both as code and as graphs outperform single-representation learners **Transfer of Learning** (Perkins & Salomon, 1992): - Far transfer (math → programming) requires explicit bridging - ggblab provides the bridge: geometric dependency → programming scope **Constructivism** (Piaget): - Students construct knowledge by connecting new concepts to existing schema - Geometric dependencies are existing schema (learned in math class) - Programming scopes are new concepts (connected via ggblab) --- ## Summary **ggblab's core educational mission** is to use geometric scene construction as a **visual, intuitive model for variable scoping**—a concept poorly taught in Python and essential for computational thinking. **The insight**: - Geometric dependencies (points → lines → circles → ...) are isomorphic to programming scopes (global → function → nested → ...) - Students already understand geometric dependencies from math class - **ggblab bridges the gap**: Export GeoGebra → visualize dependency graph → translate to Python scopes **The pipeline**: $$\text{Geometry (intuitive)} \xrightarrow{\text{ggblab}} \text{Dependency Graph (visual)} \xrightarrow{\text{mapping}} \text{Programming Scopes (abstract)}$$ **Expected outcome**: Students who learn scoping via ggblab will: 1. **Understand scope boundaries** (not just memorize LEGB) 2. **Reason about dependencies** (debug faster) 3. **Transfer to other languages** (scoping principles are universal) 4. **Connect math and CS** (computational thinking in both domains) This is not just a tool; it's a **pedagogy** grounded in cognitive science and mathematical reasoning.