User Tools

Site Tools


at:tutorial:multiparadigm

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
at:tutorial:multiparadigm [2007/06/19 11:53]
tvcutsem *
at:tutorial:multiparadigm [2011/06/07 18:29]
tvcutsem *minor
Line 1: Line 1:
-====== On Scoping, Closures, Methods and Messages ====== 
  
-<note>This tutorial chapter is still under construction</note> 
- 
-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. 
- 
-===== Lexical Scope vs Object Scope ===== 
- 
-AmbientTalk distinguishes between two kinds of scopes: 
-  - the **lexical scope**, which is the set of all variables that are lexically visible in the program text. In other words: all variables in an enclosing scope are part of the lexical scope of the enclosed (nested) scope. 
-  - the **object scope**, which is delimited by a chain of delegating objects. When sending a message to an object, the object and its parent objects delimit the scope in which the message is looked up. 
- 
-The rules for distinguishing which scope to use when resolving an identifier are straightforward: 
-  - Unqualified access to a variable, e.g. ''x'', is **always** resolved in the lexical scope. 
-  - Qualified access to a variable, e.g. ''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.m()'' 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: 
- 
-<code> 
-def o := object: { 
-  def x := 5; 
-  def getStatic() { x }; 
-  def getDynamic() { self.x }; 
-} 
-</code> 
- 
-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: 
- 
-<code> 
-def o2 := extend: o with: { 
-  def x := 6; 
-} 
-</code> 
- 
-This program behaves as follows: 
-<code> 
->o.getStatic() 
->> 5 
->o.getDynamic() 
->> 5 
->o2.getStatic() 
->> 5 
->o2.getDynamic() 
->> 6 
-</code> 
- 
-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. 
- 
-<note> 
-For many object-oriented programmers, this distinction between performing ''m()'' and ''self.m()'' 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 ''m()'' with ''self.m()'' or ''this.m()''. 
-</note> 
- 
-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. It can access both scopes by either qualifiying identifiers or not. 
- 
-Second, the difference between early and late binding offers objects a very fine-grained control over what field and method accesses may be trapped and overridden by child objects. For example, in the code snippet above, ''o'' can rest assured that the access to ''x'' in ''getStatic'' cannot be overridden by child objects (of course, child objects can still override ''getStatic''). Similarly, all points where late binding takes effect become explicit in the code as self-sends. 
- 
-==== Nesting Objects ==== 
- 
-AmbientTalk exploits its lexical scoping rules to the fullest extent by allowing as much program elements as possible to be nested. For example, it is possible to lexically nest objects within other objects, or even to nest functions within other functions. In this section, we describe how nested objects interact with the scoping rules presented above. 
- 
-One of the most appealing use cases for nesting objects is that it allows a very secure kind of //sharing// between objects: all objects that are nested within another object have the privilege of all sharing the same lexical scope. This form of sharing is much more secure that sharing data via delegation, because the set of objects sharing the scope is statically fixed. In the [[www.erights.org|E language]], such nested objects are called [[http://www.erights.org/elib/capability/ode/ode-objects.html|facets]]. 
- 
-As an example, the following code snippet defines a ''cell'' object into which one may write a value or from which one may read a value: 
- 
-<code> 
-def cell := object: { 
-  def contents := nil; 
-  def reader := object: { 
-    def read() { contents } 
-  }; 
-  def writer := object: { 
-    def write(x) { contents := x } 
-  }; 
-} 
-</code> 
- 
-The advantage of having defined ''reader'' and ''writer'' as nested objects is that they may be handed over to parts of an application such that an application has e.g. read-only or write-only access to the ''cell''. For example: 
- 
-<code> 
-distrustedFunction(cell.reader); 
-</code> 
- 
-The ''distrustedFunction'' only has a reference to the ''reader'' object, allowing it to only invoke the ''read'' method. Because ''reader'' is lexically nested within the ''cell'', it has privileged access to the ''cell'''s fields, which it shares //only// with the ''writer'' object. If ''reader'' and ''writer'' would have shared ''cell'' by means of delegation, the ''distrustedFunction'' would still be able to access the ''contents'' field by means of delegation. Because ''reader'' is lexically nested within ''cell'' and the lexical scope is inaccessible to regular objects, the ''cell'' object remains entirely encapsulated w.r.t. ''distrustedFunction''. 
- 
-==== Methods vs Closures ==== 
- 
-==== External Methods ==== 
- 
-===== First-class Methods ===== 
- 
-===== First-class Messages ===== 
at/tutorial/multiparadigm.txt ยท Last modified: 2011/06/07 18:29 by tvcutsem