This page lists some of AmbientTalk's most characteristic expressions, showing how the language aids the programmer in writing applications for distributed computing.
Here is how to send a message to a remote object, receive a reply, and deal with the exception that the reply was not received in time:
import /.at.lang.futures; when: lightbulb<-toggleLight()@Due(seconds(10)) becomes: { |reply| // code to execute when the message has been // succesfully processed. // reply refers to the return value of the // toggleLight method. } catch: TimeoutException using: { |e| // code to execute when the light bulb did not // respond before 10 seconds // note: this block may be executed either because // the message toggleLight could not be sent or was // not succesfully acknowledged, or because the message // carrying the reply was not received, or has not yet // been received (missed the deadline) }
The expression obj←msg()
denotes an asynchronous message send. The @Due
annotation puts a timeout on this asynchronous send.
To make an object available to other actors in the ad hoc network, execute:
def pub := export: obj as: Type;
where obj
is the object to be exported, and Type
refers to a type tag by means of which the object can be discovered. To unexport the object, execute pub.cancel()
. Here's how to discover the object:
def sub := when: Type discovered: { |obj| // code to execute upon discovery }
Here, obj
is either a far reference to the object (i.e. a remote object reference) if obj
is pass-by-reference, or a copy of the object, if obj
is pass-by-copy. sub
refers to a subscription object. To stop looking for a Type
object in the ad hoc network, invoke sub.cancel()
.
The above block is triggered upon discovering one object of the appropriate (sub)type. If one wants to trigger a block every time an object of the appropriate type is discovered, execute:
whenever: Type discovered: { |obj| // code to execute upon every discovery }
Given a far reference ref
to a remote object, here's how to use AmbientTalk's built in failure detector to react to the object going offline:
def sub := when: ref disconnected: { // code to execute upon disconnection }
And here is how to react to the object becoming available again:
def sub := when: ref reconnected: { // code to execute upon reconnection }
In both cases, sub
refers to a subscription object. Invoking sub.cancel()
cancels the subscription of the block with the far reference, after which it will not be invoked anymore until the block is explicitly re-registered.
In both of the above cases, the block is fired only once. If the block must be fired every time a disconnection/reconnection occurs, use the whenever:disconnected:
and whenever:reconnected:
variants.
Here's how to create a lease for an object:
import /.at.lang.leasedrefs; def l := lease: minutes(10) for: obj;
Here, l
is a leased reference: a proxy to obj
which remains valid for at least 10 minutes. Every time a message is sent via the lease to the object, the lease gets transparently renewed. When a lease has expired, it no longer acts as a proxy for obj
, allowing obj
to be eventually reclaimed if no other objects refer to it. One can react to the expiration of a lease as follows:
when: l expired: { // code to execute upon lease expiration }
Here's how to postpone the execution of a block of code until a certain period of time has elapsed:
import /.at.support.timer; def sub := when: seconds(10) elapsed: { // code to execute after 10 seconds }
Here, sub
refers to a subscription object. Invoking sub.cancel()
unregisters the block with the timer, such that it will not be executed in the future. If you want to repeatedly execute a block of code, write:
whenever: minutes(1) elapsed: { // code to execute every minute }