This shows you the differences between two versions of the page.
Both sides previous revision Previous revision Next revision | Previous revision | ||
at:tutorial:modular [2007/04/24 17:26] tvcutsem added |
at:tutorial:modular [2013/05/17 20:24] tvcutsem adjusted |
||
---|---|---|---|
Line 1: | Line 1: | ||
- | ====== Modular Programming ====== | ||
- | < | ||
- | This Tutorial is still under heavy construction! | ||
- | </ | ||
- | |||
- | In this tutorial chapter, we introduce AmbientTalk' | ||
- | |||
- | ===== Namespaces and the lobby ===== | ||
- | |||
- | In AmbientTalk, | ||
- | |||
- | ==== Namespaces ==== | ||
- | In order for AmbientTalk programs to use code defined in other files, a file loading mechanism is required. AmbientTalk provides the concept of a namespace object to perform this task. A namespace object is an object which is internally connected to a directory path on the local file system (how a namespace object is tied to this path will be explained later). By accessing a namespace object' | ||
- | |||
- | < | ||
- | object: { | ||
- | def factorial(n) { | ||
- | if: (n = 0) then: { | ||
- | 1 | ||
- | } else: { | ||
- | n*factorial(n-1) | ||
- | } | ||
- | }; | ||
- | def fib(n) { | ||
- | if: (n <= 1) then: { | ||
- | n | ||
- | } else: { | ||
- | fib(n-1)+fib(n-2) | ||
- | } | ||
- | }; | ||
- | }; | ||
- | </ | ||
- | |||
- | This file can now be loaded by means of the '' | ||
- | < | ||
- | def mathModule := lib.math; | ||
- | system.println(mathModule.factorial(5)); | ||
- | </ | ||
- | |||
- | When the namespace object' | ||
- | * a file named '' | ||
- | * a subdirectory named '' | ||
- | In this example, the file '' | ||
- | |||
- | If the '' | ||
- | |||
- | < | ||
- | By representing hierarchical directories as nested namespace objects, the AmbientTalk programmer can abstract over the actual files and directories of the underlying file system. For example, imagine that the designer of the '' | ||
- | |||
- | Obviously, this approach works both ways: if the library designer had started out organizing his project using subdirectories and multiple files, he can always restructure the library' | ||
- | </ | ||
- | |||
- | ==== The lobby ==== | ||
- | |||
- | We have yet to explain how namespaces are initially tied to directories. One possibility is to define a '' | ||
- | < | ||
- | root.home.ambienttalkuser.examples.math | ||
- | </ | ||
- | |||
- | The downside of such as scheme is that the absolute path name of the '' | ||
- | |||
- | The [[at: | ||
- | |||
- | < | ||
- | iat -o lib=/ | ||
- | </ | ||
- | |||
- | Whenever a new actor is created by the AmbientTalk interpreter, | ||
- | |||
- | < | ||
- | AmbientTalk provides an alias for the '' | ||
- | </ | ||
- | |||
- | ==== The current namespace ==== | ||
- | |||
- | AmbientTalk provides a slot named '' | ||
- | |||
- | < | ||
- | def discreteMathModule := ~.discretemath; | ||
- | </ | ||
- | |||
- | Loading a module this way is useful because the author of '' | ||
- | |||
- | ===== Importing objects ===== | ||
- | |||
- | The previous section has shown how the result of evaluating the content of an external file can be accessed from within another file. One file almost always defines multiple useful function or object definitions. As shown previously, multiple definitions can be returned to clients loading the file by returning an object and making the definitions fields or methods of the returned object. We sometimes refer to such objects as //module objects//, although they are ordinary objects with no special properties. | ||
- | |||
- | As shown in the example code above, functionality from a module object can be used simply by accessing its fields or invoking one of its methods. Again, it is not wrong to think of any object as a small kind of module. Sometimes, functionality from a module object is used so often in an importing file that it is worth redefining the function such that it can be accessed unqualified. For example, if a lot of factorials have to be calculated, it is worth defining: | ||
- | |||
- | < | ||
- | def factorial := mathModule.factorial; | ||
- | </ | ||
- | |||
- | The factorial function has been selectively // | ||
- | |||
- | < | ||
- | import mathModule; | ||
- | </ | ||
- | |||
- | is equivalent to: | ||
- | |||
- | < | ||
- | def factorial := mathModule.factorial; | ||
- | def fib := mathModule.fib; | ||
- | </ | ||
- | |||
- | When an object has been imported, all of its local slots become lexically visible. This, of course, introduces the danger that previously lexically visible definitions become // | ||
- | |||
- | < | ||
- | **Warning: | ||
- | < | ||
- | import lib; | ||
- | system.println(math.factorial(5)); | ||
- | </ | ||
- | |||
- | However, it is advised not to import namespace objects like this. The reason is that a namespace object is initially empty, and adds slots to itself dynamically whenever a slot corresponding to a file or subdirectory is accessed for the first time. Hence, importing a namespace object often boils down to importing an empty object, obviously not the semantics the programmer had in mind. Loading an entire namespace object is not supported as it would encompass loading all unloaded files in the namespace' | ||
- | </ | ||
- | |||
- | ===== Objects as traits ===== | ||
- | |||
- | In this section, we describe how the '' | ||
- | |||
- | ==== import as trait composition ==== | ||
- | |||
- | As an example, consider the typical functionality of an " | ||
- | |||
- | < | ||
- | def Enumerable := object: { | ||
- | // map the closure over the collection | ||
- | def collect: clo { | ||
- | def result := []; | ||
- | self.each: { |e| result := result + [clo(e)] }; | ||
- | result | ||
- | }; | ||
- | // return an element in enumeration for which pred returns true | ||
- | def detect: pred { | ||
- | { |return| | ||
- | self.each: { |e| | ||
- | if: pred(e) then: return(e) | ||
- | }; | ||
- | nil }.escape(); | ||
- | }; | ||
- | // return all elements for which pred returns false | ||
- | def reject: pred { | ||
- | def result := []; | ||
- | self.each: { |e| if: !pred(e) then: { result := result + [e] } }; | ||
- | result | ||
- | }; | ||
- | } | ||
- | </ | ||
- | |||
- | As can be seen from the method bodies of the '' | ||
- | |||
- | Using the '' | ||
- | |||
- | < | ||
- | def Range := object: { | ||
- | import Enumerable; | ||
- | def start := 0; | ||
- | def end := 0; | ||
- | def init(from, | ||
- | start := from; end := to; | ||
- | }; | ||
- | def each: clo { | ||
- | start.to: end do: clo | ||
- | }; | ||
- | }; | ||
- | Range.new(0, | ||
- | >> | ||
- | </ | ||
- | |||
- | In this example, '' | ||
- | |||
- | < | ||
- | def Range := object: { | ||
- | // import Enumerable is translated into: | ||
- | def collect: clo { Enumerable^collect: | ||
- | def detect: pred { Enumerable^detect: | ||
- | def reject: pred { Enumerable^reject: | ||
- | ... // previous definitions from Range | ||
- | }; | ||
- | </ | ||
- | |||
- | So, '' | ||
- | |||
- | ==== Resolving conflicts: exclusion and aliasing ==== | ||
- | |||
- | One of the advantages of trait composition is that any conflicts (name clashes) are resolved at trait composition time. Contrast this with e.g. mixin-based composition, | ||
- | |||
- | ===== Classifying objects using stripes ===== | ||
- | |||
- | explain: what are stripes? what kind of objects are they, stripe subtyping, stripe test, what default stripes exist | ||
- | |||
- | ===== Exception Handling ===== | ||
- | |||
- | explain: raise, try-catch and variants, first-class handlers, role of stripes, interface of an exception object |