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 20:59] tvcutsem * |
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 194: | 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: := 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. | 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. | ||
- | ===== Classifying objects using stripes | + | < |
+ | '' | ||
+ | * '' | ||
+ | * '' | ||
+ | |||
+ | 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' | 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, | + | Nevertheless, |
< | < | ||
- | defstripe | + | deftype |
</ | </ | ||
- | Since a stripe | + | 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, |
< | < | ||
- | defstripe | + | deftype |
- | defstripe | + | deftype |
- | defstripe | + | deftype |
</ | </ | ||
- | When defining an object, the object can be striped (tagged) with one or more stripes. | + | When defining an object, the object can be tagged with one or more type tags. |
< | < | ||
def Array := object: { | def Array := object: { | ||
... | ... | ||
- | } stripedWith: [ Indexable, Sortable ] | + | } taggedAs: [ Indexable, Sortable ] |
</ | </ | ||
- | Finally, the most useful operation defined on stripes | + | 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 |
< | < | ||
- | is: Array stripedWith: Indexable | + | is: Array taggedAs: Indexable |
>> true | >> true | ||
- | is: Array stripedWith: Ordered | + | is: Array taggedAs: Ordered |
>> true | >> true | ||
- | is: Array stripedWith: Set | + | is: Array taggedAs: Set |
>> false | >> false | ||
</ | </ | ||
- | The stripe | + | The type test determines whether an object //or one of its parents// is tagged |
- | The stripes | + | The type tags with which an object has been tagged can be retrieved by means of the '' |
< | < | ||
- | stripesOf: Array | + | tagsOf: Array |
- | >> [ <stripe: | + | >> [ <type tag: |
</ | </ | ||
< | < | ||
- | Stripes | + | 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 Stripes ==== | ||
- | The module ''/ | + | ==== Native Type Tags ==== |
+ | |||
+ | The module ''/ | ||
< | < | ||
- | is: 1 stripedWith: /.at.stripes.Number | + | is: 1 taggedAs: /.at.lang.types.Number |
>> true | >> true | ||
- | is: " | + | is: " |
>> true | >> true | ||
</ | </ | ||
- | The stripe | + | The type ''/ |
- | ==== Stripes | + | ==== Type tags as annotated message sends ==== |
- | In AmbientTalk, | + | In AmbientTalk, |
< | < | ||
- | obj.m(a, | + | obj.m(a, |
- | obj.m(a, | + | obj.m(a, |
</ | </ | ||
- | In the [[at: | + | In the [[at: |
===== 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' |