User Tools

Site Tools


at:tutorial:appendix

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
Next revisionBoth sides next revision
at:tutorial:appendix [2008/07/10 15:19] – added tvcutsemat:tutorial:appendix [2008/07/10 16:22] – added tvcutsem
Line 344: Line 344:
  
 The above code defines an isolate object ''i'' which, when passed between actors, becomes a ''some.Object'' on the other side. Note that state (''foo'' in the example) can be transferred as usual via the parameter list of the closure. The above code defines an isolate object ''i'' which, when passed between actors, becomes a ''some.Object'' on the other side. Note that state (''foo'' in the example) can be transferred as usual via the parameter list of the closure.
 +
 +===== Custom Exceptions =====
 +
 +The module ''/.at.exceptions'' defines a number of auxiliary methods which can be used to define one's own custom exceptions. Here is how to define a custom exception ''FooException''. First, define a new type tag with which clients of your code can catch the exception:
 +
 +<code>
 +deftype FooException;
 +</code>
 +
 +Next, define a prototype exception object using the ''createException'' function exported by the exception module. As a convention, an exception prototype object is prefixed with ''X'':
 +
 +<code>
 +def XFooException := createException(FooException);
 +</code>
 +
 +''XFooException'' is now bound to an object which is tagged with the given type tag, and which implements two methods: ''stackTrace'', which returns an AmbientTalk stack trace for the exception, and ''message'', which returns a string indicating what went wrong. The object also has a constructor taking a new message as an argument. You can now raise your custom exception as follows:
 +
 +<code>
 +raise: XFooException.new("reason for what went wrong");
 +</code>
 +
 +If your custom exception requires additional state, you can define it as an extension of the prototype exception. If you define a custom constructor, do not forget to initialise the parent object, as follows:
 +
 +<code>
 +deftype IndexOutOfBounds;
 +def XIndexOutOfBounds := createException(IndexOutOfBounds) with: {
 +  def min;
 +  def max;
 +  def idx;
 +  def init(min, max, idx) {
 +    super^init("Index out of bounds: given " + idx + " allowed: [" + min + "," + max + "]");
 +    self.min := min;
 +    self.max := max;
 +    self.idx := idx;
 +  }; 
 +}
 +</code>
 +
 +The exception module also exports an auxiliary function ''error(msg)'' which can be used to raise a "quick and dirty" runtime exception with a given message. It also exports the prototypes of a number of standard exceptions that can be raised by the language runtime itself.
 +
 +===== Language Extensions =====
 +
 +The files in the ''at/lang'' directory define custom language features which mostly use AmbientTalk/2's reflective facilities to extend the language.
 +
 +==== Futures and Multifutures ====
 +
 +=== Futures ===
 +
 +The module ''/.at.lang.futures'' provides support for futures. Futures have already been described as part of the [[concurreny|concurrency]] section in the tutorial.
 +
 +The module exports the type tags ''OnewayMessage'', ''FutureMessage'' and ''Due'':
 +  * Tagging an asynchronous message with ''FutureMessage'' will attach a future to the message.
 +  * Tagging a message with ''OnewayMessage'' ensures no future will ever be attached to the message.
 +  * Tagging a message with ''@Due(timeout)'' associates a future with the message that is automatically ruined with a ''TimeoutException'' after the given ''timeout'' period (in milliseconds) has elapsed.
 +
 +Messages can be automatically associated with a future by invoking the ''enableFutures()'' function, which enables futures for all messages, except those tagged as a ''OnewayMessage''.
 +
 +The futures module also exports the function ''when:becomes:'' to await the resolution of a future, and auxiliary ''when:becomes:catch:using:'' functions.
 +
 +Futures can also be created and resolved manually:
 +<code>
 +import /.at.lang.futures;
 +def [fut, res] := makeFuture();
 +when: someAsynchronousComputation() becomes: { |value|
 +  res.resolve(value); // resolve the future manually
 +}
 +fut // return the future to a client
 +</code>
 +
 +Finally, the futures module also provides some auxiliary functions, of which ''group:'' is often a very useful one. The ''group:'' construct groups a table of futures into a single future which is resolved with a table of values or ruined with an exception:
 +
 +<code>
 +when: (group: [ a<-m(), b<-n() ]) becomes: { |values|
 +  def [aResult, bResult] := values;
 +  ...
 +}
 +</code>
 +
 +=== Multifutures ===
 +
 +The module ''/.at.lang.multifutures'' provides support for multifutures. A multifuture is a future that can be resolved multiple times. We distinguish between 'bounded multifutures', which can be resolved up to a maximum number and 'unbounded multifutures' which have no upper bound.
 +
 +A multifuture is constructed as follows:
 +<code>
 +def [mf, resolver] := makeMultiFuture(n, timeout);
 +</code>
 +
 +The parameter ''n'' indicates the maximum number of values/exceptions with which the future can be resolved/ruined. If ''n'' is ''nil'', the multifuture is unbounded. The timeout parameter is optional. If not nil, it is a timeout period in milliseconds that causes the multifuture to //automatically// become fully resolved after the provided timeout. Once fully resolved, a multifuture will not accept any new values/exceptions, even if it has not reached its "upper bound" ''n'' yet.
 +
 +A multifuture accepts the following listeners:
 +
 +<code>
 +whenEach: multiFuture becomes: { |val| ... }
 +</code>
 +
 +The above listener is invoked whenever the future is resolved with a new value. Its code can thus be executed multiple times.
 +
 +<code>
 +whenAll: multiFuture resolved: { |values|
 +  ...
 +} ruined: { |exceptions| ... }
 +</code>
 +
 +The above listener is invoked if all results have been gathered (only possible if the maximum number of results is known) or when the ''timeout'' period associated with the future has elapsed. ''values'' refers to a table of all resolved values. If there are no exceptions, only the first code block is triggered. If there are only exceptions, the first block is still invoked with an empty table.
 +
 +Note the following properties of multifutures:
 +  * It is allowed to register a whenAll:resolved:ruined: listener an 'unbounded' multifuture. However, for such multifutures, this listener will only trigger if a timeout was specified during the multifuture's creation. The listener is invoked upon timeout, and later incoming results are discarded.
 +  * As with futures, it is legal to send asynchronous messages to the multifuture, which are in turn propagated to all resolved values. If some values are ruined, asynchronous messages containing a multifuture are ruined. Hence, exceptions only propagate through a pipeline of multifutures.
 +  * When a multifuture A is resolved with a multifuture B, all of B's eventual values/exceptions become values/exceptions of A.
 +  * A whenEach:becomes: observer automatically returns a multifuture itself. This multifuture has the same arity as the original and is resolved/ruined with the return values of the multiple invocations of the becomes: or catch: closures.
 +  * Like with futures, multifutures can be explicitly created, e.g.:
 +<code>def [ multifut, resolver ] := makeMultiFuture(upperBound);</code>
 +  * Multifutures can be attached to messages by annotating an asynchronous message with the @Gather(n) type tag.
 +  * Adding a when:becomes: listener on a multifuture is allowed but only triggers for the first value/exception of the multifuture. This allows multifutures to be used wherever regular futures are expected.
 +
 +The multifutures module also exports an abstraction known as a "multireference". The expression ''multiref: [ ref1, ref2,... ]'', where ''refi'' are far references, returns a multireference. Any message sent to a multireference is sent to all constituent references, and a multifuture is returned which can trap the results.
 +
 +When the message sent to a multireference is annotated with @Due(t), the timeout is applied to the implicit multifuture, causing whenAll observers to trigger automatically. Note that the implicit multifuture of a multireference is bounded, so whenAll observers trigger automatically when all replies have been received.
 +
 +==== Dynamic Variables ====
 +
 +The module ''/.at.lang.dynvars'' provides support for defining and using 'Dynamic Variables'. Dynamic variables 'simulate' dynamically scoped variables and are often used to parameterize large parts of code. For example, the 'current output stream'. A dynamic variable has the advantage over a simple global variable that it can only be assigned a value for the extent of a block of code.
 +
 +A dynamic variable can be defined as follows:
 +<code>
 +def name := dynamicVariable: initialValue;
 +</code>
 +
 +It can be read as follows:
 +<code>
 +?name or name.value
 +</code>
 +
 +It can be assigned only within a limited dynamic scope, as follows:
 +<code>
 +with: name is: newval do: { code }
 +// or
 +name.is: newval in: { code }
 +</code>
 +
 +When ''code'' terminates (either normally or via an exception), the dynamic variable is automatically reset to its previous value.
 +
 +By convention, we prefix the names of dynamic variables with a ''d'', e.g. ''dTimeoutPeriod''. This makes it easier to remember to access these variables by means of ''?'' or ''.value''.
 +
 +You can find more usage examples of dynamic variables in the unit test included in the file ''at/lang/dynvars.at''.
 +
 +==== Ambient References ====
 +
 +Ambient references are defined in the module ''/.at.lang.ambientrefs'' . An ambient reference is a special kind of far reference which refers to an ever-changing collection of objects of a certain type. For example:
 +
 +<code>
 +import /.at.lang.ambientrefs;
 +deftype Printer;
 +def printers := ambient: Printer;
 +</code>
 +
 +In the above code, ''printers'' refers to all nearby objects exported by means of  the ''Printer'' type tag. An more in-depth explanation of ambient references can be found on the [[:research:ambientrefs|research page of ambient references]].
 +
 +Ambient references ship with two so-called "implementation modules": the module /.at.ambient.ar_extensional_impl'' amd the module ''/.at.m2mi.ar_intensional_impl''. By default, the extensional implementation is used, but this can be changed by passing the desired implementation module as a parameter to the ''/.at.lang.ambientrefs'' module.
 +
 +==== Structural Types ====
 +
 +==== Traits ====
at/tutorial/appendix.txt · Last modified: 2021/09/24 10:28 by elisag