User Tools

Site Tools


at:tutorial:objects

Differences

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

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
at:tutorial:objects [2007/07/10 22:29] – * tvcutsemat:tutorial:objects [2025/06/19 16:09] (current) – Removing uniform access information elisag
Line 94: Line 94:
   def z := 0;   def z := 0;
   def sumOfSquares() {   def sumOfSquares() {
-    super^sumOfSquares() + z*z+    super^sumOfSquares() + z*z;
   };   };
 } }
Line 159: Line 159:
 def Enumerable := object: { def Enumerable := object: {
   def collect: closure {   def collect: closure {
-    def c := clone: self;+    def c := self.new([]);
     self.each: { |v|     self.each: { |v|
-      c.add(closure(v))+      c.add(closure(v));
     };     };
 +    c;
   };   };
 }; };
 def Array := object: { def Array := object: {
   def elements := [];   def elements := [];
-  def init() { ... };+  def init(a) { elements := a; }; 
 +  def add(v) { elements := elements + [v]; self };
   def collect: closure {   def collect: closure {
     Enumerable^collect: closure;     Enumerable^collect: closure;
Line 173: Line 175:
   def each: clo {   def each: clo {
     1.to: elements.length do: { |i|     1.to: elements.length do: { |i|
-      clo(elements[i])+      clo(elements[i]);
     };     };
   };   };
Line 181: Line 183:
 A message sent to an object using the ''^'' symbol (e.g. to the ''Enumerable'' object in the example above) will start the method lookup in this object and execute the method body with the ''self'' pseudovariable **left unchanged** to the message sender. In the code example above, when ''collect:'' is invoked on an ''Array'' object, the array object //delegates// the message to the ''Enumerable'' object. As such, method lookup starts in ''Enumerable'', finds the method there, and then invokes it with ''self'' left bound to the ''Array'' object. Hence, the ''self.each:'' send in the ''Enumerable'' object uses ''Array'''s definition of ''each:'' to generate the elements in the collection. A message sent to an object using the ''^'' symbol (e.g. to the ''Enumerable'' object in the example above) will start the method lookup in this object and execute the method body with the ''self'' pseudovariable **left unchanged** to the message sender. In the code example above, when ''collect:'' is invoked on an ''Array'' object, the array object //delegates// the message to the ''Enumerable'' object. As such, method lookup starts in ''Enumerable'', finds the method there, and then invokes it with ''self'' left bound to the ''Array'' object. Hence, the ''self.each:'' send in the ''Enumerable'' object uses ''Array'''s definition of ''each:'' to generate the elements in the collection.
  
-Of course, the example above is a bit contrived: we could have just assigned ''Enumerable'' as the parent of ''Array'' such that we would not even have to write the "delegating" ''collect:'' method in ''Array''. However, what the explicit ''^'' delegation operator allows is the expression of patterns resembling //multiple inheritance// where some requests are delegated to one object, while other methods can be delegated to other objects. Explicit delegation enables the expression of delegation patterns which would be awkward or difficult to express using only single (delegation-based) inheritance. In [[:at:tutorial:modular|a later chapter]], we will show how ''^'' forms the basis for advanced //trait composition//.+<code> 
 +Array.add(1).add(2).add(3) 
 +def c := Array.collect: { |v| v+1 } 
 +c.each: { |v| system.print(v)} // prints 234 
 +</code> 
 + 
 +Of course, the example above is a bit contrived: we could have just assigned ''Enumerable'' as the parent of ''Array'' such that we would not even have to write the "delegating" ''collect:'' method in ''Array''. However, what the explicit ''^'' delegation operator allows is the expression of patterns resembling //multiple inheritance// where some requests are delegated to one object, while other methods can be delegated to other objects. Explicit delegation enables the expression of delegation patterns which would be awkward or difficult to express using only single (delegation-based) inheritance. In [[:at:tutorial:modular#objects_as_traits|a later chapter]], we will show how ''^'' forms the basis for advanced //trait composition//.
  
 Having described the semantics of ''^'', we can now turn our attention to "super-sends". In AmbientTalk, a traditional "super-send" (ala Java or Smalltalk) is expressed as explicit delegation to the ''super'' object. For example, here is how to properly initialize parent objects from child objects: Having described the semantics of ''^'', we can now turn our attention to "super-sends". In AmbientTalk, a traditional "super-send" (ala Java or Smalltalk) is expressed as explicit delegation to the ''super'' object. For example, here is how to properly initialize parent objects from child objects:
Line 221: Line 229:
 > makeBankAccount(100).balance; > makeBankAccount(100).balance;
 >>Lookup failure : selector balance could not be found in  >>Lookup failure : selector balance could not be found in 
-  <object:5068254>+  <obj:{super,super:=,deposit}>
 </code> </code>
 +
 +This pattern of creating objects by means of "constructor functions" rather than by cloning and instantiating prototypes is often very useful in its own right. However, when creating objects that use their lexical scope to hold their state, be aware of the following issue when you mix object creation via instantiation and object creation via cloning: clones //share// their lexical scope! Hence, given a bank account object ''b'', the following leads to erroneous behaviour:
 +
 +<code>
 +def b := makeBankAccount(100);
 +def b2 := b.new(); // shares its balance field with b!
 +b.deposit(10); // affects b2 as well!
 +</code>
 +
 +In order to prevent this kind of errors, it is considered best practice to override ''new'' for objects that should be solely defined by means of constructor functions, as follows:
 +
 +<code>
 +def makeBankAccount(balance) {
 +  object: {
 +    def new(@args) { makeBankAccount(@args) };
 +    def deposit(amnt) { /* as before */ };
 +  }
 +}
 +</code>
 +
 +By overriding ''new'' and calling the constructor function, this code ensures that code such as ''aBankAccount.new(10)'' will result in a proper new bank account object with its own private lexical scope to store its state.
 +
at/tutorial/objects.1184099376.txt.gz · Last modified: 2007/07/10 22:31 (external edit)