at:tutorial:multiparadigm
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revisionNext revisionBoth sides next revision | ||
at:tutorial:multiparadigm [2007/06/19 10:57] – tvcutsem | at:tutorial:multiparadigm [2008/03/13 16:53] – updated tvcutsem | ||
---|---|---|---|
Line 1: | Line 1: | ||
====== On Scoping, Closures, Methods and Messages ====== | ====== On Scoping, Closures, Methods and Messages ====== | ||
- | |||
- | < | ||
This tutorial chapter goes into a bit more detail on the subtle interplay between AmbientTalk' | This tutorial chapter goes into a bit more detail on the subtle interplay between AmbientTalk' | ||
- | ===== Scoping | + | ===== Lexical Scope vs Object Scope ===== |
- | ==== Nesting Objects ==== | + | 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. | ||
- | ==== Lexical vs Object Scope ==== | + | The rules for distinguishing which scope to use when resolving an identifier are straightforward: |
+ | - Unqualified access to a variable, e.g. '' | ||
+ | - Qualified access to a variable, e.g. '' | ||
- | ==== Methods vs Closures ==== | + | These rules also hold for method invocation: the invocation '' |
- | ==== External Methods ==== | + | Probably the most important consequence 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, '' | ||
+ | |||
+ | < | ||
+ | def o2 := extend: o with: { | ||
+ | def x := 6; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | This program behaves as follows: | ||
+ | < | ||
+ | > | ||
+ | >> 5 | ||
+ | > | ||
+ | >> 5 | ||
+ | > | ||
+ | >> 5 | ||
+ | > | ||
+ | >> 6 | ||
+ | </ | ||
+ | |||
+ | As can be derived from the rules defined above, the access to '' | ||
+ | |||
+ | <note important> | ||
+ | For many object-oriented programmers, | ||
+ | </ | ||
+ | |||
+ | The distinction between '' | ||
+ | |||
+ | 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, '' | ||
+ | |||
+ | The difference between unqualified and qualified slot access is also important to child objects. If a child object wants to invoke an inherited method of its parent object, it must invoke the method by means of a self-send. For example, consider AmbientTalk' | ||
+ | |||
+ | < | ||
+ | def myTest := extend: UnitTest with: { | ||
+ | def testSomething() { | ||
+ | self.assertEquals(1+1, | ||
+ | }; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | It is important that '' | ||
+ | |||
+ | ===== 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. | ||
+ | |||
+ | ==== Facets ==== | ||
+ | |||
+ | 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 E language, such nested objects are called [[http:// | ||
+ | |||
+ | As an example, the following code snippet defines a '' | ||
+ | |||
+ | < | ||
+ | def cell := object: { | ||
+ | def contents := nil; | ||
+ | def reader := object: { | ||
+ | def read() { contents } | ||
+ | }; | ||
+ | def writer := object: { | ||
+ | def write(x) { contents := x } | ||
+ | }; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | The advantage of having defined '' | ||
+ | |||
+ | < | ||
+ | distrustedFunction(cell.reader); | ||
+ | </ | ||
+ | |||
+ | The '' | ||
+ | |||
+ | ==== Nesting and Delegation ==== | ||
+ | |||
+ | Thanks to AmbientTalk' | ||
+ | |||
+ | < | ||
+ | def myWindow := extend: Window with: { | ||
+ | def title := " | ||
+ | def myButton := extend: Button with: { | ||
+ | // myButton can access lexical scope and | ||
+ | // its own object scope | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | It can sometimes happen that a nested object wants to invoke a method dynamically (rather than statically) on one of its outer objects. This is possible by aliasing '' | ||
+ | |||
+ | < | ||
+ | extend: UnitTest with: { | ||
+ | def testSomething() { | ||
+ | def theTest := self; | ||
+ | def inner := object: { | ||
+ | def compare(a, | ||
+ | theTest.assertEquals(a, | ||
+ | } | ||
+ | }; | ||
+ | inner.compare(1+1, | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | In the example above, writing '' | ||
+ | |||
+ | ===== Methods vs Closures ===== | ||
+ | |||
+ | As mentioned previously, AmbientTalk not only allows objects to be nested within other objects, but also allows functions to be nested within other functions. At this point, it becomes important to distinguish between // | ||
+ | |||
+ | In AmbientTalk, | ||
+ | |||
+ | In AmbientTalk, | ||
+ | |||
+ | Methods and closures are defined by means of the same syntax. They are distinguishable only by the context in which they are evaluated. For example: | ||
+ | |||
+ | < | ||
+ | def o := object: { | ||
+ | def x := 5; | ||
+ | def meth(y) { | ||
+ | def clo(z) { self.x + y + z } | ||
+ | }; | ||
+ | }; | ||
+ | > o.meth(2)(1); | ||
+ | >> 8 | ||
+ | </ | ||
+ | |||
+ | In the above example, '' | ||
+ | |||
+ | ==== Top-level Functions ==== | ||
+ | |||
+ | Top-level functions are actually methods in AmbientTalk. This is because all top-level code is treated as the initialization code of that file's module object. Hence, it is legal to use '' | ||
+ | |||
+ | ==== Block closures ==== | ||
+ | |||
+ | Block closures, as created by means of the syntax '' | ||
+ | |||
+ | ===== External Methods | ||
+ | |||
+ | It is possible to define methods and fields externally on an object. For example: | ||
+ | |||
+ | < | ||
+ | def calc := object: { | ||
+ | def add(x,y) { x+y }; | ||
+ | }; | ||
+ | def offset := 0; | ||
+ | def calc.sub(x, | ||
+ | </ | ||
+ | |||
+ | In this example '' | ||
+ | |||
+ | In AmbientTalk, | ||
+ | |||
+ | <note important> | ||
+ | There is one more difference between closures and external methods: next to the special treatment of '' | ||
+ | |||
+ | The rationale behind this decision is that '' | ||
+ | </ | ||
===== First-class Methods ===== | ===== First-class Methods ===== | ||
+ | |||
+ | In AmbientTalk, | ||
+ | |||
+ | Methods become first-class when they are selected from existing objects. Because of the [[at: | ||
+ | |||
+ | < | ||
+ | def Math := object: { | ||
+ | def square(x) { x*x }; | ||
+ | }; | ||
+ | |||
+ | def squareClo := Math.& | ||
+ | > squareClo(2) | ||
+ | >> 4 | ||
+ | </ | ||
+ | |||
+ | Slot selection automatically lifts a method slot into a closure. The closure can subsequently be passed around as a stand-alone value. When the method closure is invoked, the original method is ran with '' | ||
+ | |||
+ | < | ||
+ | def Adder := object: { | ||
+ | def amount := 0; | ||
+ | def init(amnt) { | ||
+ | amount := amnt; | ||
+ | }; | ||
+ | def addTo(x) { | ||
+ | x + self.amount | ||
+ | }; | ||
+ | }; | ||
+ | > [1, | ||
+ | >> [2,3,4] | ||
+ | </ | ||
+ | |||
+ | The '' | ||
+ | |||
+ | The selection operator is also required when accessing lexically visible methods as first-class closures: | ||
+ | |||
+ | < | ||
+ | def o := object: { | ||
+ | def m() { ... }; | ||
+ | def grabTheMethod() { &m }; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Selection using ''&'' | ||
+ | |||
+ | < | ||
+ | def o := object: { | ||
+ | x := 5; | ||
+ | }; | ||
+ | def f := o.&x; | ||
+ | > f | ||
+ | >> <native closure: | ||
+ | > o.x := 6; f() | ||
+ | >> 6 | ||
+ | </ | ||
+ | |||
+ | In the same vein, one may select a mutator method for a field '' | ||
===== First-class Messages ===== | ===== First-class Messages ===== | ||
+ | |||
+ | Next to first-class methods, AmbientTalk also supports first-class messages. Messages are naturally represented as objects. They encapsulate a receiver, a selector and a table of actual arguments. AmbientTalk is definitely not the first language to treat messages as first-class citizens this way. For example, Smalltalk equally treats messages as objects. | ||
+ | |||
+ | The main difference with other languages is that AmbientTalk provides an expressive syntax to define first-class messages and to enable the sending of a first-class message to an object. In AmbientTalk, | ||
+ | |||
+ | < | ||
+ | def msg := .add(1,2); | ||
+ | > | ||
+ | >>< | ||
+ | > | ||
+ | >> | ||
+ | </ | ||
+ | |||
+ | This syntax is supported for all of AmbientTalk' | ||
+ | |||
+ | An AmbientTalk message supports the operation '' | ||
+ | |||
+ | < | ||
+ | calc <+ msg | ||
+ | </ | ||
+ | |||
+ | The ''< | ||
+ | |||
+ | First-class messages are often useful to implement [[http:// |
at/tutorial/multiparadigm.txt · Last modified: 2011/06/07 18:29 by tvcutsem