Exercise 3: A Three-Level Inheritance Chain — Possible Solution ==================================================================== THE PROGRAM ------------------------------ class A { whoAmI() { return "A"; } } class B < A { } class C < B { } var c = C(); print c.whoAmI(); RESULT ------------------------------ A TRACING find_method("whoAmI") STARTING FROM C ------------------------------ C.find_method('whoAmI'): 'whoAmI' not in C.methods (C defines no methods of its own) C.superclass is B (not None) -> return B.find_method('whoAmI') [call 1] B.find_method('whoAmI'): 'whoAmI' not in B.methods (B defines no methods of its own either) B.superclass is A (not None) -> return A.find_method('whoAmI') [call 2] A.find_method('whoAmI'): 'whoAmI' IS in A.methods -> return A.methods['whoAmI'] [found -- recursion stops here] Three total calls to find_method() (one per class in the chain, C then B then A), matching the chain depth exactly -- two levels of "not found here, ask the superclass" before the third call finally succeeds. THE NOT-FOUND-ANYWHERE CASE ------------------------------ class A {} class B < A {} var b = B(); print b.nope(); -> WispRuntimeError: undefined property 'nope' TRACING THIS CASE ------------------------------ B.find_method('nope'): 'nope' not in B.methods B.superclass is A -> return A.find_method('nope') [call 1] A.find_method('nope'): 'nope' not in A.methods A.superclass is None -> return None [base case: chain exhausted] Two calls total (B then A) before find_method() returns None all the way back up. WispInstance.get() then sees `method is None` and raises the WispRuntimeError itself -- find_method() never raises anything on its own; returning None is its own well-defined "not found anywhere" signal, and it's the CALLER's job (WispInstance.get(), not find_method()) to turn that into an actual error. WHY THIS WORKS AS AN ANSWER ------------------------------ find_method()'s own recursion depth is always exactly equal to how many classes have to be checked before either finding the method or exhausting the chain -- one call per class, walking strictly upward, never sideways or back down. This is structurally identical to Chapter 5's own Environment.get() walking its enclosing chain: both are a linked structure (superclass here, enclosing there) searched by one recursive call per link, stopping either at a definitive "found it" or a definitive "the chain ran out" (self.superclass is None here, self.enclosing is None there). The number of hops in either case is never more than the actual structural depth of what's being searched -- there's no way for either lookup to do unnecessary work by revisiting a link it's already checked.