at:tutorial:appendix
Differences
This shows you the differences between two versions of the page.
| Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
| at:tutorial:appendix [2008/07/10 14:07] – * tvcutsem | at:tutorial:appendix [2024/10/03 22:19] (current) – fixing path to exceptions module elisag | ||
|---|---|---|---|
| Line 1: | Line 1: | ||
| - | ====== Appendix ====== | + | ====== Appendix: Libraries |
| - | In the appendix, we explain useful libraries available to the AmbientTalk/ | + | In the appendix, we explain useful libraries available to the AmbientTalk/ |
| + | |||
| + | The Ambientalk standard library ('' | ||
| ===== Unit Testing Framework ===== | ===== Unit Testing Framework ===== | ||
| Line 27: | Line 29: | ||
| This will execute all '' | This will execute all '' | ||
| - | Like in JUnit and SUnit, it is possible to define two methods named '' | + | Like in JUnit and SUnit, it is possible to define two methods named '' |
| ==== Assertions ==== | ==== Assertions ==== | ||
| Line 82: | Line 84: | ||
| It is also possible to use '' | It is also possible to use '' | ||
| + | |||
| + | <note tip> | ||
| + | See the [[distribution# | ||
| + | </ | ||
| ==== Test Suites ==== | ==== Test Suites ==== | ||
| Line 95: | Line 101: | ||
| The '' | The '' | ||
| + | |||
| + | ===== Basic Collections ===== | ||
| + | |||
| + | The modules ''/ | ||
| + | |||
| + | ==== Vector ==== | ||
| + | |||
| + | A vector is a dynamically resizable AmbientTalk table (aka array). Indexed reading from and writing to a vector is fast (O(1)). Adding elements to a vector is mostly fast, but sometimes requires a resize of the vector. Vectors support the traditional stack operations '' | ||
| + | |||
| + | Vectors may be created as follows: | ||
| + | |||
| + | < | ||
| + | import / | ||
| + | def v := Vector.new(10); | ||
| + | </ | ||
| + | |||
| + | The constructor optionally takes a comparator as a second argument. A comparator is a binary function returning a boolean whose job is to compare elements of the Vector. This comparator is used among others when sorting the vector. The Vector' | ||
| + | |||
| + | < | ||
| + | // returns the number of elements in the vector (not its capacity!) | ||
| + | length() | ||
| + | |||
| + | // returns whether the vector contains elements or not | ||
| + | isEmpty() | ||
| + | |||
| + | // is the vector at max. capacity? | ||
| + | atMaxCapacity() | ||
| + | |||
| + | // return idx'th element or raise an IndexOutOfBounds exception | ||
| + | at(idx) | ||
| + | |||
| + | // write idx'th element or raise IndexOutOfBounds exception | ||
| + | atPut(idx, val) | ||
| + | |||
| + | // iterate over the vector | ||
| + | each: iterator, returns nil | ||
| + | |||
| + | // map a unary function over the vector, returns a new vector | ||
| + | map: fun | ||
| + | |||
| + | // accumulate a function with a given starting value | ||
| + | inject: init into: accum; | ||
| + | |||
| + | // returns a new vector whose elements satisfy " | ||
| + | filter: cond; | ||
| + | |||
| + | // implode a vector of character strings into one text string | ||
| + | implode() | ||
| + | |||
| + | // join a vector of character strings together with the given string | ||
| + | join(txt) | ||
| + | |||
| + | // returns a range [start, | ||
| + | select(start, | ||
| + | |||
| + | // appends an element to the back of the vector. Returns the vector itself | ||
| + | add(element) | ||
| + | // alias for add(element) | ||
| + | << | ||
| + | |||
| + | // insert an element at a given position, causing subsequent elements to shift one pos to the right. Returns this vector | ||
| + | insert(atPos, | ||
| + | |||
| + | // delete the element at the given position, shifts all following elements one pos to the left. Returns the value of the element at the deleted position. | ||
| + | delete(atPos) | ||
| + | |||
| + | // adds elements to the back of the vector | ||
| + | push(element) | ||
| + | |||
| + | // deletes elements from the back of the vector | ||
| + | pop() | ||
| + | |||
| + | // return the index of the first element matching the unary predicate or nil if none is found | ||
| + | find: filter | ||
| + | |||
| + | // remove the given element from the vector, return true if the element was actually found and deleted, false otherwise | ||
| + | remove(elt, cmp := defaultComparator) | ||
| + | |||
| + | // remove all objects for which filter(elt) returns true | ||
| + | removeAll: filter | ||
| + | |||
| + | // destructively appends otherVector to self. Returns this vector | ||
| + | addAll(otherVector) | ||
| + | |||
| + | // empties the vector | ||
| + | clear() | ||
| + | |||
| + | // Return a new vector whose elements form the set-union of all elements in self U otherVector | ||
| + | union(otherVector, | ||
| + | |||
| + | // Return a new vector whose elements form the set-intersection of all elements in self ^ otherVector | ||
| + | intersection(otherVector, | ||
| + | |||
| + | // Return a new vector whose elements form the set-difference of self \ otherVector | ||
| + | difference(otherVector, | ||
| + | |||
| + | // Quicksort the vector in-place. The comparator defines the ordering among elements. | ||
| + | sort(cmp := { |e1,e2| e1 < e2 }) | ||
| + | |||
| + | // Turn the vector into a set without duplicates in O(nlogn + n) | ||
| + | // The vector' | ||
| + | uniq(cmp := defaultComparator, | ||
| + | |||
| + | // return an element drawn randomly using a uniform distribution from the array or raise an EmptyVector exception. | ||
| + | random() | ||
| + | |||
| + | // return a table containing all elements of the vector | ||
| + | asTable() | ||
| + | </ | ||
| + | |||
| + | The file '' | ||
| + | |||
| + | ==== List ==== | ||
| + | |||
| + | The module ''/ | ||
| + | |||
| + | The list module defines two styles to manipulate cons-cells: an object-oriented and a functional style. The object-oriented style represents cons-cells as '' | ||
| + | |||
| + | The functional style allows one to manipulate lists by means of the following functions: | ||
| + | |||
| + | < | ||
| + | cons(car, | ||
| + | car(conscell) -> the car | ||
| + | cdr(conscell) -> the cdr | ||
| + | list(@items) -> a cons-cell representing the head of a list | ||
| + | </ | ||
| + | |||
| + | Lists (cons-cells or the empty list) support the following operations: | ||
| + | |||
| + | < | ||
| + | // accessors for car and cdr | ||
| + | car() | ||
| + | cdr() | ||
| + | |||
| + | // the length of the list | ||
| + | length() | ||
| + | |||
| + | // whether the list is empty or not | ||
| + | isEmpty() | ||
| + | |||
| + | // returns the nth element of the list | ||
| + | nth(n) | ||
| + | |||
| + | // apply a unary function to each element of the list | ||
| + | each: fun | ||
| + | |||
| + | // apply a function to each element and its index in the list | ||
| + | // i.e. list.eachWithIndex: | ||
| + | eachWithIndex: | ||
| + | |||
| + | // map a unary function over the list, returning a new list | ||
| + | map: fun | ||
| + | |||
| + | // accumulate a value over a list | ||
| + | inject: init into: accum | ||
| + | |||
| + | // return a new list whose elements satisfy the unary predicate | ||
| + | filter: cond | ||
| + | |||
| + | // does the list contain the element? | ||
| + | contains(elt, | ||
| + | |||
| + | // implode or join a list of text strings | ||
| + | implode() | ||
| + | join(txt) | ||
| + | |||
| + | // drop the first n elements from the list | ||
| + | tail(n) | ||
| + | |||
| + | // prepend an element to the list | ||
| + | add(elt) | ||
| + | |||
| + | // insert an element in the list (functionally) | ||
| + | insert(atPos, | ||
| + | |||
| + | // return a new list where the element atPos is deleted | ||
| + | delete(atPos) | ||
| + | |||
| + | // functional append | ||
| + | append(aList) | ||
| + | |||
| + | // return the index of the first matching element, or nil if none is found | ||
| + | find: filter | ||
| + | |||
| + | // return the index in the list of the element or nil of not found | ||
| + | indexOf(elt, | ||
| + | |||
| + | // return a list where the given element is removed | ||
| + | remove(elt, cmp := DEFAULTCOMPARATOR) | ||
| + | |||
| + | // return a new list where all objects for which filter(elt) is true are removed | ||
| + | removeAll: filter | ||
| + | |||
| + | // convert the list into a table | ||
| + | asTable() | ||
| + | </ | ||
| + | |||
| + | The file '' | ||
| + | |||
| + | |||
| + | ===== Top-level functions ===== | ||
| + | |||
| + | The file '' | ||
| + | |||
| + | ==== Asynchronous control structures ==== | ||
| + | |||
| + | The '' | ||
| + | |||
| + | '' | ||
| + | < | ||
| + | loop: { | ||
| + | ... | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | An '' | ||
| + | < | ||
| + | whenTrue: booleanFuture then: { ... } else: { ... } | ||
| + | </ | ||
| + | |||
| + | Asynchronous while loop over future-type conditional: | ||
| + | < | ||
| + | asLongAs: { /* asynchronous computation returning a future */ } do: { ... } | ||
| + | </ | ||
| + | |||
| + | ==== Mobile code ==== | ||
| + | |||
| + | The function '' | ||
| + | |||
| + | < | ||
| + | def mobileAdder(x) { | ||
| + | script: { |n| x + n } carrying: [`x] | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | A call to '' | ||
| + | |||
| + | The constructor function '' | ||
| + | |||
| + | < | ||
| + | def foo := 42; | ||
| + | def i := isolate: { | ||
| + | ... | ||
| + | } passAs: { |foo| | ||
| + | / | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | The above code defines an isolate object '' | ||
| + | |||
| + | ===== Custom Exceptions ===== | ||
| + | |||
| + | The module ''/ | ||
| + | |||
| + | < | ||
| + | deftype FooException; | ||
| + | </ | ||
| + | |||
| + | Next, define a prototype exception object using the '' | ||
| + | |||
| + | < | ||
| + | def XFooException := createException(FooException); | ||
| + | </ | ||
| + | |||
| + | '' | ||
| + | |||
| + | < | ||
| + | raise: XFooException.new(" | ||
| + | </ | ||
| + | |||
| + | If your custom exception requires additional state, you can define it as an extension of the prototype exception. If you define a custom constructor, | ||
| + | |||
| + | < | ||
| + | deftype IndexOutOfBounds; | ||
| + | def XIndexOutOfBounds := createException(IndexOutOfBounds) with: { | ||
| + | def min; | ||
| + | def max; | ||
| + | def idx; | ||
| + | def init(min, max, idx) { | ||
| + | super^init(" | ||
| + | self.min := min; | ||
| + | self.max := max; | ||
| + | self.idx := idx; | ||
| + | }; | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | The exception module also exports an auxiliary function '' | ||
| + | |||
| + | ===== Language Extensions ===== | ||
| + | |||
| + | The files in the '' | ||
| + | |||
| + | |||
| + | |||
| + | |||
| + | =====Futures and Multifutures ===== | ||
| + | |||
| + | ==== Futures ==== | ||
| + | |||
| + | The module ''/ | ||
| + | |||
| + | The module exports the type tags '' | ||
| + | * Tagging an asynchronous message with '' | ||
| + | * Tagging a message with '' | ||
| + | * Tagging a message with '' | ||
| + | |||
| + | Messages can be automatically associated with a future by invoking the '' | ||
| + | |||
| + | The futures module also exports the function '' | ||
| + | |||
| + | Futures can also be created and resolved manually: | ||
| + | < | ||
| + | import / | ||
| + | def [fut, res] := makeFuture(); | ||
| + | when: someAsynchronousComputation() becomes: { |value| | ||
| + | res.resolve(value); | ||
| + | } | ||
| + | fut // return the future to a client | ||
| + | </ | ||
| + | |||
| + | The '' | ||
| + | |||
| + | === Auxilary functions in the futures module ==== | ||
| + | |||
| + | The futures module also provides some auxiliary functions, of which '' | ||
| + | |||
| + | < | ||
| + | when: (group: [ a< | ||
| + | def [aResult, bResult] := values; | ||
| + | ... | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | Another useful auxilary function is '' | ||
| + | |||
| + | < | ||
| + | future: { |return| | ||
| + | // some computation | ||
| + | return(val) | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | This is actually equivalent to the slightly more verbose code: | ||
| + | |||
| + | < | ||
| + | def [fut,res] := makeFuture(); | ||
| + | try: { // some computation | ||
| + | res.resolve(val); | ||
| + | } catch: Exception using: { |e| res.ruin(e) } | ||
| + | fut; | ||
| + | </ | ||
| + | |||
| + | ==== Multifutures ==== | ||
| + | |||
| + | The module ''/ | ||
| + | |||
| + | A multifuture is constructed as follows: | ||
| + | < | ||
| + | def [mf, resolver] := makeMultiFuture(n, | ||
| + | </ | ||
| + | |||
| + | The parameter '' | ||
| + | |||
| + | A multifuture accepts the following listeners: | ||
| + | |||
| + | < | ||
| + | whenEach: multiFuture becomes: { |val| ... } | ||
| + | </ | ||
| + | |||
| + | The above listener is invoked whenever the future is resolved with a new value. Its code can thus be executed multiple times. | ||
| + | |||
| + | < | ||
| + | whenAll: multiFuture resolved: { |values| | ||
| + | ... | ||
| + | } ruined: { |exceptions| ... } | ||
| + | </ | ||
| + | |||
| + | The above listener is invoked if all results have been gathered (only possible if the maximum number of results is known) or when the '' | ||
| + | |||
| + | Note the following properties of multifutures: | ||
| + | * It is allowed to register a whenAll: | ||
| + | * As with futures, it is legal to send asynchronous messages to the multifuture, | ||
| + | * When a multifuture A is resolved with a multifuture B, all of B's eventual values/ | ||
| + | * A whenEach: | ||
| + | * Like with futures, multifutures can be explicitly created, e.g.: | ||
| + | < | ||
| + | * Multifutures can be attached to messages by annotating an asynchronous message with the @Gather(n) type tag. | ||
| + | * Adding a when: | ||
| + | |||
| + | The multifutures module also exports an abstraction known as a " | ||
| + | |||
| + | When the message sent to a multireference is annotated with @Due(t), the timeout is applied to the implicit multifuture, | ||
| + | |||
| + | |||
| + | ===== Leased Object References ===== | ||
| + | |||
| + | The module ''/ | ||
| + | |||
| + | < | ||
| + | The implementation of leased object references actually consists of two files: ''/ | ||
| + | </ | ||
| + | |||
| + | The '' | ||
| + | * '' | ||
| + | * '' | ||
| + | * '' | ||
| + | |||
| + | Variations of these constructs are also provided to allow developers to specify the renewal time interval in renew-on-call leased references and the name(s) of the method(s) which trigger expiration of a single-call leased reference. | ||
| + | |||
| + | The '' | ||
| + | |||
| + | < | ||
| + | renew: leasedRef for: interval; // renews a lease | ||
| + | revoke: leasedRef; // revokes a lease | ||
| + | leaseTimeLeft: | ||
| + | when: lease expired: {...}; // trigger a closure when the lease expires | ||
| + | </ | ||
| + | |||
| + | The '' | ||
| + | |||
| + | Finally, the '' | ||
| + | |||
| + | |||
| + | |||
| + | ===== TOTAM ===== | ||
| + | |||
| + | The module ''/ | ||
| + | |||
| + | Please have a look to [[: | ||
| + | |||
| + | ===== Dynamic Variables ===== | ||
| + | |||
| + | The module ''/ | ||
| + | |||
| + | A dynamic variable can be defined as follows: | ||
| + | < | ||
| + | def name := dynamicVariable: | ||
| + | </ | ||
| + | |||
| + | It can be read as follows: | ||
| + | < | ||
| + | ?name or name.value | ||
| + | </ | ||
| + | |||
| + | It can be assigned only within a limited dynamic scope, as follows: | ||
| + | < | ||
| + | with: name is: newval do: { code } | ||
| + | // or | ||
| + | name.is: newval in: { code } | ||
| + | </ | ||
| + | |||
| + | When '' | ||
| + | |||
| + | By convention, we prefix the names of dynamic variables with a '' | ||
| + | |||
| + | You can find more usage examples of dynamic variables in the unit test included in the file '' | ||
| + | |||
| + | ===== Ambient References ===== | ||
| + | |||
| + | Ambient references are defined in the module ''/ | ||
| + | |||
| + | < | ||
| + | import / | ||
| + | deftype Printer; | ||
| + | def printers := ambient: Printer; | ||
| + | </ | ||
| + | |||
| + | In the above code, '' | ||
| + | |||
| + | Ambient references ship with two so-called " | ||
| + | |||
| + | ===== Structural Types ===== | ||
| + | |||
| + | The module ''/ | ||
| + | |||
| + | A structural type can be branded with type tags. In this case, objects only conform to the type if they are structurally conformant **and** if they are tagged with the structural type's brands. | ||
| + | |||
| + | Use the '' | ||
| + | |||
| + | < | ||
| + | def PersonProtocol := protocol: { | ||
| + | def name; | ||
| + | def age; | ||
| + | } named: `Person; | ||
| + | </ | ||
| + | |||
| + | The '' | ||
| + | |||
| + | < | ||
| + | def tom := object: { | ||
| + | def name := " | ||
| + | def age() { 24 }; | ||
| + | } implements: PersonProtocol; | ||
| + | </ | ||
| + | |||
| + | You can also create a protocol from an object: | ||
| + | < | ||
| + | |||
| + | You can test protocol conformance in either of two styles: | ||
| + | * '' | ||
| + | * '' | ||
| + | |||
| + | You can also force a '' | ||
| + | * '' | ||
| + | * '' | ||
| + | |||
| + | More usage examples of structural types can be found in the unit test defined in the file '' | ||
| + | |||
| + | ===== Traits ===== | ||
| + | |||
| + | The module ''/ | ||
| + | |||
| + | Using the '' | ||
| + | |||
| + | To define a " | ||
| + | < | ||
| + | trait: { | ||
| + | ... | ||
| + | } requiring: Protocol; | ||
| + | </ | ||
| + | |||
| + | The above code creates a trait that can only be composed into an object adhering to the specified protocol. To compose traits, use the following language construct: | ||
| + | |||
| + | < | ||
| + | object: { | ||
| + | use: { | ||
| + | import T1 exclude ...; | ||
| + | import T2 alias ...; | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | The '' | ||
| + | |||
| + | Note that the place where '' | ||
| + | |||
| + | Usage examples can be found in the unit tests in the file '' | ||
| + | |||
| + | ===== Utilities ===== | ||
| + | |||
| + | The files in the '' | ||
| + | |||
| + | |||
| + | ==== Timing Utilities ==== | ||
| + | |||
| + | The module ''/ | ||
| + | |||
| + | < | ||
| + | def subscription := when: timeoutPeriod elapsed: { | ||
| + | ... | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | The '' | ||
| + | |||
| + | The milliseconds used to define the timeout period must be provided as a Java '' | ||
| + | |||
| + | * '' | ||
| + | * '' | ||
| + | * '' | ||
| + | |||
| + | Additionally, | ||
| + | |||
| + | The timer module also defines a function '' | ||
| + | |||
| + | Finally, there is a variant of '' | ||
| + | |||
| + | < | ||
| + | def testAsyncNearbyPlayerReply(){ | ||
| + | def nearbyPlayers := // search 2 nearby player orjbects; | ||
| + | // wait a bit so that there are the 2 members. | ||
| + | when: 2.seconds elapsedWithFuture: | ||
| + | self.assertEquals(2, | ||
| + | } | ||
| + | }; | ||
| + | </ | ||
| + | |||
| + | |||
| + | ==== Logging Framework ==== | ||
| + | |||
| + | The module ''/ | ||
| + | |||
| + | Here's a typical example of how to use a logger: | ||
| + | < | ||
| + | import / | ||
| + | def log := makeLogger(" | ||
| + | log("a message", | ||
| + | </ | ||
| + | |||
| + | The '' | ||
| + | |||
| + | The logging level determines which messages are shown on the output log. The available error levels are: '' | ||
| + | |||
| + | The output object is an object that understands '' | ||
| + | |||
| + | ==== Object Inspector ==== | ||
| + | |||
| + | The module ''/ | ||
| + | |||
| + | < | ||
| + | import / | ||
| + | inspect(o); | ||
| + | </ | ||
| + | |||
| + | This will pop up a graphical inspector on the object, listing the object' | ||
| + | |||
| + | ==== Symbiosis Utilities ==== | ||
| + | |||
| + | The module ''/ | ||
| + | |||
| + | < | ||
| + | long(anAmbientTalkNumber) -> aJavaLong | ||
| + | short(anAmbientTalkNumber) -> aJavaShort | ||
| + | float(anAmbientTalkFraction) -> aJavaFloat | ||
| + | byte(anAmbientTalkNumber) -> aJavaByte | ||
| + | </ | ||
| + | |||
| + | The module also defines the following function: | ||
| + | < | ||
| + | cast: obj into: Interface | ||
| + | </ | ||
| + | |||
| + | The '' | ||
| + | |||
| + | ==== Miscellaneous ==== | ||
| + | |||
| + | The module ''/ | ||
| + | |||
| + | === Random Numbers === | ||
| + | |||
| + | The utility module defines functions for easily generating random numbers. Its implementation uses the random number generators from the underlying JVM. The following functions are the most useful: | ||
| + | |||
| + | < | ||
| + | // generate a random integer in the interval [min, max[ | ||
| + | def randomNumberBetween(min, | ||
| + | // generate a random fraction in the interval [min, max[ | ||
| + | def randomFractionBetween(min, | ||
| + | </ | ||
| + | |||
| + | === Custom Object Serialization === | ||
| + | |||
| + | The method '' | ||
| + | |||
| + | < | ||
| + | //inside a mirror | ||
| + | def instancevar := ...; | ||
| + | def pass() { | ||
| + | uponArrivalBecome: | ||
| + | // return object to become here | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | The function plays a role similar to '' | ||
at/tutorial/appendix.1215691667.txt.gz · Last modified: (external edit)
