How Are Bare Names Interpreted?
A bare name in code like
i can be interpreted as either a variable in some surrounding scope, or as a getter on
this. It could correctly be both of those at the same time, so it's important to know which takes priority and when.
I believe the expected behavior is:
var i = "global" print(i) // global defclass C var i = "getter" end do var i = "outer" def (this C) method() -> print(i) // getter var i = "local" print(i) // local do var i = "shadow" print(i) // shadow end print(i) // local end end
So local variables within the method take priority over getters, which take priority over variables declared in the method's closure. Lookup then is:
- Look in the current local scope.
- Walk up local scopes until we hit the top scope of the function body.
- Look on
this.
- Look in the scope chain where the method is declared (its closure).
- If not found, throw an
UnknownNameError.
That's fine for things with only a single level of nesting. Now consider:
var i = "global" defclass C var i = "getter" end def (this C) method1() var i1 = "1" def (this C) method2() var i2 = "2" def (this C) method3() var i3 = "3" print(i1) // "1" print(i2) // "2" print(i3) // "3" print(notfound) end end end
When we look up
notfound here, it needs to:
- Look in the locals of
method3.
- Look for a getter on
thisin
method3.
- Look in the locals of
method2.
- Look for a getter on
thisin
method2.
- Look in the locals of
method1.
- Look for a getter on
thisin
method1.
- Look in the global scope.
So we need either a way to arbitrarily interleave lookup of variables and getters in the scope chain, or we need to restrict the scope in which methods can be defined (which is kind of lame).
This gets more confusing because getters themselves are defined in the scope chain. It's just that we may need to "find" them earlier when walking the scope chain. For example:
// 1 defclass C var i = "getter" end // getter is defined here in scope chain do // 2 var i = "inner" def (this C) method() // 3 print(i) end end
Here we have three nested scopes (labelled
// 1,
// 2,
// 3). The getter for
i is defined in
1, the outermost scope. There is a variable
i declared in
2. Inside
3, we print
i. The tricky bit is that it needs to find the getter first, so we need to give priority to the
i in
1 over the nearer one in
2. Confusing.
Not Interleaved
Actually, the above isn't correct. When looking up a name, we never have to look at
this in an outer method definition. At any point in code, there is only one
this, and that's the one we need to consider. They don't cascade. So with:
def (this A) method1() def (this B) method2() def (this C) method3() print(foo) end end end
When we're looking up
foo, all we need to consider are:
- The locals in
method3.
- Methods specialized to
thisin
method3.
- Variables in the closure of
method3.
The
this definitions in
method2 and
method1 never come into play.