This is an old revision of the document!
This tutorial chapter goes into a bit more detail on the subtle interplay between AmbientTalk's functional aspects (e.g. block closures, higher-order functions and lexical scoping) and its object-oriented aspects (e.g. objects and delegation). It is also described how methods and messages can be manipulated as first-class objects in their own right.
AmbientTalk distinguishes between two kinds of scopes:
The rules for distinguishing which scope to use when resolving an identifier are straightforward:
x
, is always resolved in the lexical scope.o.x
, is always resolved in the receiver's object scope.
These rules also hold for method invocation: the invocation f()
is resolved lexically: f
is looked up in the lexical scope; the invocation o.)
is resolved dynamically, i.e. m
is looked up in o
. These rules have a large effect on programs: lexical variable access can be statically determined, while qualified access is subject to late binding (enabling object-oriented polymorphism). As a programmer, you must be aware of the fundamental difference in semantics.
Probably the most important conseqence of these rules is that great care has to be taken when an object accesses its own fields or methods. It can now do so in two ways. For example:
def o := object: { def x := 5; def getStatic() { x }; def getDynamic() { self.x }; }
In the code snippet above, o
defines two accessors for its field x
. The getStatic
accessor refers to x
unqualified. As a result, x
is looked up in the lexical scope and found in o
. The getDynamic
accessor accesses the field by means of a self-send. According to the rules outlined above, x
is accessed in a qualified way, which means it is looked up in the object scope of o
. Now consider the following code:
def o2 := extend: o with: { def x := 6; }
This program behaves as follows:
>o.getStatic() >> 5 >o.getDynamic() >> 5 >o2.getStatic() >> 5 >o2.getDynamic() >> 6
As can be derived from the rules defined above, the access to x
is early bound: the value of x
returned by getStatic
is always the value of the lexically visible x
variable, in this case the field of o
. Qualified access, including self-sends like self.x
is resolved in the receiver's object scope. Hence, when getDynamic
is invoked on the o2
object, variable lookup starts in o2
and the overridden field's value is returned.
)
and self.)
may seem confusing and even error-prone. After all, merely “forgetting” the qualified access disallows child objects to override the invocation to m
. The confusion stems from the fact that many OO languages – like Java – make no distinction between both access forms because they are not truly lexically scoped. They only consider the object scope, and hence unify )
with self.)
or this.)
.
The distinction between x
and self.x
, although at first sight potentially confusing, offers many advantages. First, because AmbientTalk has true lexical scoping rules, we feel that any lexically visible variable should be made accessible to the programmer. For example, when nesting objects, the nested object should have access to the fields and methods of the outer object, next to its own fields and methods.