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] (current) tvcutsem adjusted |
||
---|---|---|---|
Line 1: | Line 1: | ||
====== Modular Programming ====== | ====== Modular Programming ====== | ||
- | |||
- | < | ||
- | This Tutorial is still under heavy construction! | ||
- | </ | ||
In this tutorial chapter, we introduce AmbientTalk' | In this tutorial chapter, we introduce AmbientTalk' | ||
Line 61: | Line 57: | ||
The downside of such as scheme is that the absolute path name of the '' | The downside of such as scheme is that the absolute path name of the '' | ||
- | The [[at: | + | The [[at: |
< | < | ||
Line 68: | Line 64: | ||
Whenever a new actor is created by the AmbientTalk interpreter, | Whenever a new actor is created by the AmbientTalk interpreter, | ||
+ | |||
+ | Think of AmbientTalk' | ||
< | < | ||
Line 108: | Line 106: | ||
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 // | 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: | **Warning: | ||
< | < | ||
Line 121: | Line 119: | ||
In this section, we describe how the '' | In this section, we describe how the '' | ||
+ | |||
==== import as trait composition ==== | ==== import as trait composition ==== | ||
Line 164: | Line 163: | ||
}; | }; | ||
def each: clo { | def each: clo { | ||
- | start.to: end do: clo | + | start.to: end-1 do: clo |
}; | }; | ||
}; | }; | ||
Line 183: | Line 182: | ||
</ | </ | ||
- | So, '' | + | So, '' |
+ | |||
+ | Note that in AmbientTalk, | ||
+ | |||
+ | < | ||
+ | def Enumerable := object: { | ||
+ | def collect: clo { /* as before */ }; | ||
+ | def detect: pred { /* as before */ }; | ||
+ | def reject: pred { /* as before */ }; | ||
+ | def each: clo @Required; | ||
+ | } | ||
+ | </ | ||
==== Resolving conflicts: exclusion and aliasing ==== | ==== Resolving conflicts: exclusion and aliasing ==== | ||
Line 189: | Line 199: | ||
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, | 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 | + | The '' |
+ | |||
+ | < | ||
+ | // do not import the slots collect: and detect: | ||
+ | import Enumerable exclude collect:, detect: | ||
+ | // do not import collect: and import reject: as remove: | ||
+ | import Enumerable alias reject: := remove: exclude collect: | ||
+ | </ | ||
+ | |||
+ | 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. | ||
+ | |||
+ | < | ||
+ | '' | ||
+ | * '' | ||
+ | * '' | ||
+ | |||
+ | Moreover, all methods of the trait object annotated with '' | ||
+ | </ | ||
+ | |||
+ | ===== Classifying objects using type tags ===== | ||
+ | |||
+ | In class-based languages, classes are a useful tool for categorising objects. Each object is an instance of some class, and sometimes it is useful to be able to ask to which class an object belongs. However, it is well-known by proponents of object-oriented programming that explicitly referring to the class of an object breaks that object' | ||
+ | |||
+ | Nevertheless, | ||
+ | |||
+ | < | ||
+ | deftype Indexable; | ||
+ | </ | ||
+ | |||
+ | Since a type tag is an abstract type or category, it makes sense to define subtype relations on them. Also, since type tags do not carry an implementation, | ||
+ | |||
+ | < | ||
+ | deftype Enumerable; | ||
+ | deftype Ordered; | ||
+ | deftype Sortable <: Enumerable, Ordered; | ||
+ | </ | ||
+ | |||
+ | When defining an object, the object can be tagged with one or more type tags. | ||
+ | |||
+ | < | ||
+ | def Array := object: { | ||
+ | ... | ||
+ | } taggedAs: [ Indexable, Sortable ] | ||
+ | </ | ||
+ | |||
+ | Finally, the most useful operation defined on type tags is the "type test": it allows objects to test whether an object is tagged with the appropriate type tag. | ||
+ | |||
+ | < | ||
+ | is: Array taggedAs: Indexable | ||
+ | >> true | ||
+ | is: Array taggedAs: Ordered | ||
+ | >> true | ||
+ | is: Array taggedAs: Set | ||
+ | >> false | ||
+ | </ | ||
+ | |||
+ | The type test determines whether an object //or one of its parents// is tagged with the given type tag //or a subtype// of the type tag. | ||
+ | |||
+ | The type tags with which an object has been tagged can be retrieved by means of the '' | ||
+ | |||
+ | < | ||
+ | tagsOf: Array | ||
+ | >> [ <type tag: | ||
+ | </ | ||
+ | |||
+ | < | ||
+ | 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 '' | ||
+ | </ | ||
+ | |||
+ | |||
+ | ==== Native Type Tags ==== | ||
+ | |||
+ | The module ''/ | ||
+ | |||
+ | < | ||
+ | is: 1 taggedAs: / | ||
+ | >> true | ||
+ | is: " | ||
+ | >> true | ||
+ | </ | ||
+ | |||
+ | The type ''/ | ||
+ | |||
+ | ==== Type tags as annotated message sends ==== | ||
+ | |||
+ | In AmbientTalk, | ||
+ | |||
+ | < | ||
+ | obj.m(a, | ||
+ | obj.m(a, | ||
+ | </ | ||
+ | |||
+ | In the [[at: | ||
- | explain: what are stripes? what kind of objects are they, stripe subtyping, stripe test, what default stripes exist | ||
===== Exception Handling ===== | ===== Exception Handling ===== | ||
- | explain: raise, | + | AmbientTalk employs a traditional exception handling mechanism based on try-blocks. The '' |
+ | |||
+ | < | ||
+ | try: { | ||
+ | calculateSomething(); | ||
+ | } catch: DivisionByZero using: { |e| | ||
+ | system.println(e.message); | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | The first argument is a closure to execute, delineating a dynamic piece of code to protect with the given exception handler. The third argument is a one-argument closure, invoked when an exception is caught of the right kind. The second argument is a type tag. By default, exceptions are handled based on their type tags. A hierarchy of type tags is used to classify exceptions. Exception handling in AmbientTalk is quite conventional: | ||
+ | |||
+ | Raising an exception is done by means of the '' | ||
+ | |||
+ | < | ||
+ | if: (denominator == 0) then: { | ||
+ | raise: XDivisionByZero.new(" | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Note that a new instance of an object named '' | ||
+ | |||
+ | < | ||
+ | deftype DivisionByZero <: lobby.at.lang.types.Exception; | ||
+ | def XDivisionByZero := lobby.at.lang.exceptions.createException(DivisionByZero); | ||
+ | </ | ||
+ | |||
+ | ==== Exceptions raised by the interpreter ==== | ||
+ | |||
+ | The '' | ||
+ | |||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | |||
+ | ==== Custom exception handlers ==== | ||
+ | |||
+ | You can specify any object to act as a first-class | ||
+ | |||
+ | < | ||
+ | try: { | ||
+ | calculateSomething(); | ||
+ | } using: (object: { | ||
+ | // this is a first-class handler object | ||
+ | def canHandle(exc) { | ||
+ | is: exc taggedAs: DivisionByZero | ||
+ | }; | ||
+ | def handle(exc) { | ||
+ | system.println(exc.message); | ||
+ | }; | ||
+ | } taggedAs: [/ | ||
+ | </ | ||
+ | |||
+ | First-class exception handlers are sometimes useful to factor out common exception handling behaviour, and also to specify more complex boolean conditions, e.g. one can express that only '' | ||
+ | |||
+ | ==== Catching multiple kinds of exceptions ==== | ||
+ | |||
+ | AmbientTalk defines a number of convenience methods that allow you to list up to three '' | ||
+ | |||
+ | < | ||
+ | try: { | ||
+ | calculateSomething(); | ||
+ | } catch: DivisionByZero using: { |e| | ||
+ | system.println(e.message); | ||
+ | } catch: NoSolution using: { |e| | ||
+ | calculateSomethingElse(); | ||
+ | }; | ||
+ | </ | ||
+ | |||
+ | If more clauses are required, either nested '' | ||
+ | |||
+ | Care has to be taken that handlers are listed in increasing order of " | ||
+ | |||
+ | ===== 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 '' | ||
+ | |||
+ | < | ||
+ | def contains(tbl, | ||
+ | { |return| | ||
+ | 1.to: tbl.length do: { |i| | ||
+ | if: (tbl[i] == elt) then: { | ||
+ | return(true) | ||
+ | } | ||
+ | }; | ||
+ | false | ||
+ | }.escape() | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | When '' | ||
+ | |||
+ | 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' |