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
this
inmethod3
. - Look in the locals of
method2
. - Look for a getter on
this
inmethod2
. - Look in the locals of
method1
. - Look for a getter on
this
inmethod1
. - 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
this
inmethod3
. - Variables in the closure of
method3
.
The this
definitions in method2
and method1
never come into play.