User Tools

Site Tools


at:tutorial:modular

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:modular [2007/06/19 16:06]
tvcutsem
at:tutorial:modular [2013/05/17 20:24] (current)
tvcutsem adjusted
Line 57: Line 57:
 The downside of such as scheme is that the absolute path name of the ''math.at'' file is hardcoded in the AmbientTalk application. If we would later decide to move the file, our AmbientTalk code using that file would be broken! To avoid these problems, AmbientTalk provides the ''lobby'' object. The ''lobby'' is an object defined in the top-level scope whose slots are bound to namespace objects. The names of the slots, and the directory paths of the namespace objects bound to them are not provided in AmbientTalk directly, but rather when starting the AmbientTalk interpreter. The downside of such as scheme is that the absolute path name of the ''math.at'' file is hardcoded in the AmbientTalk application. If we would later decide to move the file, our AmbientTalk code using that file would be broken! To avoid these problems, AmbientTalk provides the ''lobby'' object. The ''lobby'' is an object defined in the top-level scope whose slots are bound to namespace objects. The names of the slots, and the directory paths of the namespace objects bound to them are not provided in AmbientTalk directly, but rather when starting the AmbientTalk interpreter.
  
-The [[at:tutorial:iat|interactive ambienttalk shell]] (''iat'') has a command-line parameter called the **object path**. This argument is a list of "name=path" entries separated by colons. For example, in order to create a namespace object called ''lib'' which is bound to ''/home/ambienttalkuser/examples'', it suffices to start ''iat'' as follows:+The [[at:tutorial:iat|interactive ambienttalk shell]] (''iat'') has a command-line parameter called the **object path**. This argument is a list of "name=path" entries separated by colons on Mac/Linux or semicolons on Windows. For example, in order to create a namespace object called ''lib'' which is bound to ''/home/ambienttalkuser/examples'', it suffices to start ''iat'' as follows:
  
 <code> <code>
Line 64: Line 64:
  
 Whenever a new actor is created by the AmbientTalk interpreter, it uses the entries in the object path to initialize the ''lobby'' object of the actor. Hence, by starting up the interpreter as shown above, executing ''lobby.lib'' in any actor returns a reference to a namespace object bound to the ''examples'' directory. This effectively removes absolute path names from AmbientTalk source code and even enables you to quickly reconfigure AmbientTalk code to use other libraries by loading the code using another object path. Whenever a new actor is created by the AmbientTalk interpreter, it uses the entries in the object path to initialize the ''lobby'' object of the actor. Hence, by starting up the interpreter as shown above, executing ''lobby.lib'' in any actor returns a reference to a namespace object bound to the ''examples'' directory. This effectively removes absolute path names from AmbientTalk source code and even enables you to quickly reconfigure AmbientTalk code to use other libraries by loading the code using another object path.
 +
 +Think of AmbientTalk's object path as the equivalent of Java's classpath.
  
 <note> <note>
Line 117: Line 119:
  
 In this section, we describe how the ''import'' statement can also be used to compose multiple objects. In this respect, objects can be regarded as //traits//. [[http://www.iam.unibe.ch/~scg/Research/Traits|Traits]] are a model of object-oriented software composition, similar to (but more advanced than) mixins. A trait can be regarded as a kind of abstract class, which does not define any state, which //provides// a set of methods and which //requires// a set of methods in order to be instantiated. In a class-based language, a class may //use// one or more traits. The class takes on the role of compositor and is responsible for ensuring that, once all traits have been imported, each trait its required interface is provided by the class or other imported traits. A trait is almost literally a piece of code which can be "copy-pasted" into a class. In this section, we describe how the ''import'' statement can also be used to compose multiple objects. In this respect, objects can be regarded as //traits//. [[http://www.iam.unibe.ch/~scg/Research/Traits|Traits]] are a model of object-oriented software composition, similar to (but more advanced than) mixins. A trait can be regarded as a kind of abstract class, which does not define any state, which //provides// a set of methods and which //requires// a set of methods in order to be instantiated. In a class-based language, a class may //use// one or more traits. The class takes on the role of compositor and is responsible for ensuring that, once all traits have been imported, each trait its required interface is provided by the class or other imported traits. A trait is almost literally a piece of code which can be "copy-pasted" into a class.
 +
  
 ==== import as trait composition ==== ==== import as trait composition ====
Line 160: Line 163:
   };   };
   def each: clo {   def each: clo {
-    start.to: end do: clo+    start.to: end-1 do: clo
   };   };
 }; };
Line 179: Line 182:
 </code> </code>
  
-So, ''import'' defines small methods which delegate the request to the original trait. The use of delegation is crucial here: it means that within the context of the trait, ''self'' is bound to the object using the trait. Hence, when ''reject:'' is invoked on a ''Range'' object, ''Enumerable'''s ''self.each:'' will refer to the correct implementation of ''Range''.+So, ''import'' defines small methods which delegate the request to the original trait (using AmbientTalk's support for [[:at:tutorial:objects#first-class_delegation|explicit delegation]]). The use of delegation is crucial here: it means that within the context of the trait, ''self'' is bound to the object using the trait. Hence, when ''reject:'' is invoked on a ''Range'' object, ''Enumerable'''s ''self.each:'' will refer to the correct implementation of ''Range''. 
 + 
 +Note that in AmbientTalk, a trait does not need to explicitly specify the set of methods which it "requires" from its composite. However, for documentation purposes, it is often very useful to explicitly state the methods on which a trait depends. One may do so by defining those methods in the trait, and annotating them with the ''@Required'' type tag (which is predefined in the ''lobby.at.lang.types'' module). These methods will //not// be imported when the trait is used by a composite object (otherwise they would cause a conflict with the real implementation methods in the composite). Below is an example of how the ''Enumerable'' trait can be modified to explicitly state its required methods: 
 + 
 +<code> 
 +def Enumerable := object: { 
 +  def collect: clo { /* as before */ }; 
 +  def detect: pred { /* as before */ }; 
 +  def reject: pred { /* as before */ }; 
 +  def each: clo @Required; 
 +
 +</code>
  
 ==== Resolving conflicts: exclusion and aliasing ==== ==== Resolving conflicts: exclusion and aliasing ====
Line 190: Line 204:
 // do not import the slots collect: and detect: // do not import the slots collect: and detect:
 import Enumerable exclude collect:, detect: import Enumerable exclude collect:, detect:
-// do not import collect: and import reject: as remove +// do not import collect: and import reject: as remove: 
-import Enumerable alias reject: := remove exclude collect:+import Enumerable alias reject: := removeexclude collect:
 </code> </code>
  
 If the compositor defines an alias for an imported slot, it is good practice to ensure that the compositor has (or imports) a slot with the original name as well. That way, if the trait object performs a self-send to invoke one of its own methods, it will find a matching slot in the compositor. If the compositor aliases a slot and does not define the slot itself, a lookup by the trait of the original slot name would fail. If the compositor defines an alias for an imported slot, it is good practice to ensure that the compositor has (or imports) a slot with the original name as well. That way, if the trait object performs a self-send to invoke one of its own methods, it will find a matching slot in the compositor. If the compositor aliases a slot and does not define the slot itself, a lookup by the trait of the original slot name would fail.
 +
 +<note>
 +''import'' adds two names to the ''exclude'' clause by default:
 +  * ''super'' (the parent object) because this slot is present in //all// AmbientTalk objects.
 +  * ''~'' (the current namespace), because this slot is present in //all// namespace objects.
 +
 +Moreover, all methods of the trait object annotated with ''@Required'' are automatically excluded upon ''import''.
 +</note>
  
 ===== Classifying objects using type tags ===== ===== Classifying objects using type tags =====
Line 245: Line 267:
 Type tags can best be compared to empty Java interface types. Such empty interfaces are sometimes used in Java purely for the purposes of marking an object. Examples are ''java.io.Serializable'' and ''java.lang.Cloneable''. An empty interface type can be implemented by any class (object) and hence only serves the purpose of distinguishing objects by type (by means of ''instanceof'' in Java). Type tags can best be compared to empty Java interface types. Such empty interfaces are sometimes used in Java purely for the purposes of marking an object. Examples are ''java.io.Serializable'' and ''java.lang.Cloneable''. An empty interface type can be implemented by any class (object) and hence only serves the purpose of distinguishing objects by type (by means of ''instanceof'' in Java).
 </note> </note>
 +
  
 ==== Native Type Tags ==== ==== Native Type Tags ====
  
-The module ''/.at.types'' shipped with the system library contains the type definitions of the native data types of the interpreter. These type tags can be used to perform type tests on objects, for example:+The module ''/.at.lang.types'' shipped with the system library contains the type definitions of the native data types of the interpreter. These type tags can be used to perform type tests on objects, for example:
  
 <code> <code>
-is: 1 taggedAs: /.at.types.Number+is: 1 taggedAs: /.at.lang.types.Number
 >> true >> true
-is: "foo" taggedAs: /.at.types.Text+is: "foo" taggedAs: /.at.lang.types.Text
 >> true >> true
 </code> </code>
  
-The type ''/.at.types.Isolate'' can be used to mark an object as an isolate. Isolate objects are similar to serializable Java objects. They are explained when introducing concurrent programming in [[at:tutorial:actors|a later chapter]].+The type ''/.at.lang.types.Isolate'' can be used to mark an object as an isolate. Isolate objects are similar to serializable Java objects. They are explained when introducing concurrent programming in [[at:tutorial:actors|a later chapter]].
  
 ==== Type tags as annotated message sends ==== ==== Type tags as annotated message sends ====
Line 269: Line 292:
  
 In the [[at:tutorial:actors|chapter on actors]] we use this feature to distinguish purely asynchronous message sends from so-called //future-type// message sends. If the message send is annotated with the ''FutureMessage'' type tag, the asynchronous send returns a future, otherwise it returns ''nil''. In the [[at:tutorial:actors|chapter on actors]] we use this feature to distinguish purely asynchronous message sends from so-called //future-type// message sends. If the message send is annotated with the ''FutureMessage'' type tag, the asynchronous send returns a future, otherwise it returns ''nil''.
 +
  
 ===== Exception Handling ===== ===== Exception Handling =====
Line 292: Line 316:
 </code> </code>
  
-Note that a new instance of an object named ''XDivisionByZero'' is raised, not the type tag ''DivisionByZero'' itself. An exception is any object that is tagged with (a subtype of) the ''lobby.at.types.Exception'' type tag. By convention, an exception should have the fields ''message'' and ''stackTrace''. The exception module found under ''lobby.at.exceptions'' contains an auxiliary function named ''createException'' which takes a type tag as argument and returns a new prototype exception object. For example:+Note that a new instance of an object named ''XDivisionByZero'' is raised, not the type tag ''DivisionByZero'' itself. An exception is any object that is tagged with (a subtype of) the ''lobby.at.lang.types.Exception'' type tag. By convention, an exception should have the fields ''message'' and ''stackTrace''. The exception module found under ''lobby.at.lang.exceptions'' contains an auxiliary function named ''createException'' which takes a type tag as argument and returns a new prototype exception object. For example:
  
 <code> <code>
-deftype DivisionByZero <: lobby.at.types.Exception; +deftype DivisionByZero <: lobby.at.lang.types.Exception; 
-def XDivisionByZero := lobby.at.exceptions.createException(DivisionByZero);+def XDivisionByZero := lobby.at.lang.exceptions.createException(DivisionByZero);
 </code> </code>
  
Line 322: Line 346:
     system.println(exc.message);     system.println(exc.message);
   };   };
-})+taggedAs: [/.at.lang.types.Handler])
 </code> </code>
  
Line 335: Line 359:
   calculateSomething();   calculateSomething();
 } catch: DivisionByZero using: { |e| } catch: DivisionByZero using: { |e|
-  system.println(e.message):+  system.println(e.message);
 } catch: NoSolution using: { |e| } catch: NoSolution using: { |e|
   calculateSomethingElse();   calculateSomethingElse();
Line 344: Line 368:
  
 Care has to be taken that handlers are listed in increasing order of "generality". If the most general handler (e.g. a handler for ''Exception'') is listed first, other handlers will not have been consulted and will never trigger. Care has to be taken that handlers are listed in increasing order of "generality". If the most general handler (e.g. a handler for ''Exception'') is listed first, other handlers will not have been consulted and will never trigger.
 +
 +===== Escaping Continuations =====
 +
 +It is often useful to be able to abort the control flow within a method prematurely. In traditional imperative programming languages, this is done by means of a ''return'' statement (or the ''^'' in Smalltalk). AmbientTalk inherits a similar control structure from the Self programming language. Rather than introducing the notion of a ''return'' statement, AmbientTalk uses the more general concept of an //escaping continuation// to achieve a similar effect. Consider the following code which tests whether a given table ''tbl'' contains an element ''elt'':
 +
 +<code>
 +def contains(tbl, elt) {
 +  { |return|
 +    1.to: tbl.length do: { |i|
 +      if: (tbl[i] == elt) then: {
 +        return(true)
 +      }
 +    };
 +    false
 +  }.escape()
 +}
 +</code>
 +
 +When ''escape()'' is invoked on a block closure, the effect is to apply that block closure to a special function object (the escaping continuation). That object, named ''return'' in the example, behaves as a function of one argument. When that function is invoked, control immediately returns to the end of the block, and the value passed to the escaping continuation is the result of the ''escape()'' call. If the function object is not called, the closure returns normally and its return value is the result of the ''escape()'' call.
 +
 +As can be seen, an escaping continuation is more general than a return statement: the continuation is a first-class function object and can hence be passed on to other objects. Hence, it is possible to return from multiple nested function calls at once. 
 +
 +There is an important limitation to the use of escaping continuations. An escaping continuation is not a full-fledged continuation (such as the one provided by Scheme's ''call/cc''): the escaping continuation is valid **only** in the execution of the block closure. Once control has returned from the block (either because it terminated normally with a value, or by means of the escaping continuation), the escaping continuation can no longer be used. If the continuation function object is e.g. stored in a variable and applied later, when the block already terminated, this will result in an exception being raised.
at/tutorial/modular.1182261971.txt.gz · Last modified: 2007/06/25 20:58 (external edit)