croczilla.com 
 home   stratified   bits&pieces   blog   
  home > oni > Reference

Oni Core Reference

Syntax

Oni is an expression-based language; i.e. an Oni program consists of a single Oni expression.

Oni expressions are constructed using the Oni syntax operators

ALift, Alt, Apply, Bracket, Break, Catch, Complete, Continue, Defun, Delay, Eval, Get, GetCadddr, GetCaddr, GetCadr, GetCar, GetCdr, If, Lambda, Let, Loop, Quote, SLift, Seq, Time, and Throw,

the built-in Oni functions

AtomicVar.Create, AtomicVar.Read, AtomicVar.Write, Cadddr, Caddr, Cadr, Car, Cdr, Cons, Equals, IsNull, List, ListRef, ListTail, Member, NEquals, Nop, Not, Obj, ObjMem, Par, Remove, Sleep, and Stop,

as well as user-defined & library Oni operators/functions.

In addition, for the JavaScript Oni implementation, functional application on Oni expressions ( i.e. postfixing an Oni expression E with '( arg1, arg2, ... )') is synonymous with Apply(E, arg1, arg2, ...).

The difference between syntax operators and functions is that functions are Oni expressions in their own right, whereas syntax operators have to be applied to arguments before becoming Oni expressions.

Most Oni syntax operators and functions are combinators over a wider set of expressions ("exp's") which, in addition to Oni expressions ("oni_exp's"), admits "primitive" expressions from the host language ("p_exps").

Here is an approximation of the Oni grammar as it is currently implemented for JavaScript:

oni_exp  : oni_exp ( exp* )              |
           oni_func                      |
           ALift ( async_func )          |
           Alt ( exp* )                  |
           Apply ( exp, exp* )           |
           Bracket ( exp, exp, exp)      |
           Break ( exp? )                |
           Catch ( [ tag, exp? ], exp+ ) |
           Complete ( exp+ )             |
           Continue ( )                  |
           Defun ( formals, exp+ )       |
           Delay ( exp, exp* )           |
           Eval ( exp )                  |
           Get ( varname )               |
           GetCadddr ( varname )         |
           GetCaddr ( varname )          |
           GetCadr ( varname )           |
           GetCar ( varname )            |
           GetCdr ( varname )            |
           If ( exp, exp, exp? )         |
           Lambda ( formals, exp+ )      |
           Let ( bindings, exp+ )        |
           Loop ( exp+ )                 |
           Quote ( datum )               |
           SLift ( sync_func )           |
           Seq ( exp* )                  |
           Throw ( tag, exp? )           |
           Time ( exp+ )

oni_func : builtin_oni_func      |
           user_defined_oni_func

builtin_oni_func : AtomicVar.Create |
                   AtomicVar.Read |
                   AtomicVar.Write |
                   Add | Sub | Mul | Div |
                   Cadddr | Caddr | Cadr | Car |
                   Cdr | Cons| Equals| IsNull |
                   List | ListRef | ListTail |
                   Member | NEquals | Nop | Not |
                   Obj | ObjMem | Par| Remove |
                   Stop | Sleep

user_defined_oni_func : a host-language expression evaluating to
                        an Oni function (e.g. created using ALift,
                        SLift, Defun, Eval(Lambda(...)) or similar)

exp : oni_exp |
      p_exp

p_exp : any host language expression that does not evaluate (in the
        host language) to an oni_exp

bindings :  { varname : exp, ... }

formals : [ varname* ]

datum : a host language expression that evaluates to some object or
        primitive type

varname : a host language expression that evaluates to string

tag : a host language expression resolving to an object that will be
      compared under the host language's '==' equivalence relation

async_func : a asynchronous host language function
             (see documentation of ALift)

sync_func : a synchronous host language function
            (see documentation of SLift)

Executing Oni Programs

'Eval'

An Oni program E is executed with the operator Eval. When the expression Eval(E) is evaluated in the host language, it causes the expression E to be evaluated immediately. The actual Oni program execution is fully asynchronous, i.e. happens after Eval(E) returns.

Example:

Eval(Print(SLift(function(x,y) { return x+y; })(10, 20)));

This asynchronously prints '30' (presupposing that Print is suitably defined - it is not an Oni primitive).

Eval(E) is an Oni expression itself, i.e. can be used as part of other Oni programs: Eval(E) returns an Oni 'asynchronous value' expression which can be used in other Oni programs to access the result of evaluating E.

Example:

var A = Delay(1000, 42);
var valueOfA = Eval(A);
var B = Print(valueOfA);
var C = Print(Add(10, valueOfA));
Eval(B);
Eval(C);
Eval(Print("Test"));

Here, A is an Oni program that evaluates to the value 42 after 1 second. Both B and C are Oni programs that depend on the value of A. While all the Eval statements will return immediately, Eval(B) and Eval(C) will block internally until Eval(A) has finished evaluating and its result value is available. The output of these programs is

Test
42
52

or

Test
52
42

An Oni program can be executed multiple times by passing it to Eval multiple times, each time yielding a new independent asynchronous value object.

Expression evaluation

An Oni expression is evaluated in an asynchronous fashion, yielding a value which, for all Oni operators apart from Eval, will be passed to the expression's continuation. The continuation is formed by the enclosing Oni expression in the current lexical or dynamic scope (depending on the particular Oni operators currently being evaluated).

Evaluation terminates when a value is yielded to the Eval that started the execution. As described in the previous section, at that point Eval makes the returned value available to other Oni programs that might be waiting on it.

We call an expression 'pending' in the context of an execution context if its evaluation has been started, and it is yet to (asynchronously) yield a value to its continuation.

We designate by <exp> the value yielded by the evaluation of exp.

A pending expression can be aborted at any point as a result of the semantics of an enclosing Oni combinator (see e.g. Alt, below). Aborting an expression means that the expression's value will not be propagated to the expression's continuation. I.e. the expression reverts to a non-pending state. Furthermore, a user-defined Oni function can implement a custom abort protocol, to e.g. free resources used during the function's evaluation (see ALift, below).

Oni expressions are evaluated in a lexically scoped environment which can be modified using Let and queried using Get (see below).

Oni expressions in a tail position are evaluated in constant space (XXX elaborate what constitutes a tail position). I.e. Oni is tail-recursion safe.


Basic Control Flow

'Seq': Sequential Execution
Seq(exp_1, exp_2, ..., exp_n)

evaluates its subexpressions exp_1..n sequentially and returns the value of exp_n.

If a pending Seq() expression is aborted, the currently pending subexpression will be aborted as well.

'Par': Fork-Join Parallel Execution

The Oni function call

Par(exp_1, exp_2, ..., exp_n)

evaluates exp_1..n concurrently, waits for the evaluation of all of them to complete, and then returns control to the enclosing expression. The return value is unspecified.

If a pending Par() expression is aborted, all currently pending subexpressions will also be aborted.

'Alt': Parallel alternatives pruning
Alt(exp_1, exp_2, ..., exp_n)

begins evaluating its subexpressions exp_1..n concurrently. As soon as one of the subexpressions exp_i returns a value, all other pending subexpressions are aborted and the value <exp_i> is returned.

If a pending Alt() expression is aborted, all currently pending subexpressions will also be aborted.


Conditionals

'If'
If(test_exp, consequent_exp, alternative_exp?)

begins by evaluating test_exp. If <test_exp> is a value that evaluates to true under the rules of the host language, consequent_exp will be evaluated next, and <consequent_exp> will be returned. Otherwise, if <test_exp> is a value that evaluates to false under the rules of the host language, further processing depends on whether alternative_exp has been provided. If this is the case, alternative_exp will be executed and <alternative_exp> will be returned. Otherwise, <test_exp> will be returned.

If a pending If expression is aborted, any currently pending subexpression will be aborted, too.

Example:

If(MyTest(Get('foo')),
   Print("Foo passed the test"),
   Print("Foo failed the test"))

Looping

'Loop'
Loop(exp_1, exp_2, ..., exp_n)

evaluates exp_1..n sequentially (as in a Seq()). When exp_n returns, evaluation resumes with exp_1.

'Break'
Break(exp?)

evaluates exp (if provided) and exits the nearest enclosing Loop, with <exp> as the return value (or undefined if exp wasn't given). Pending subexpressions of Loop will be aborted.

'Continue'
Continue()

aborts pending subexpressions of the nearest enclosing Loop and resumes evaluation with exp_1.


Exceptions

'Throw'
Throw(throw_tag, throw_exp?)

evaluates throw_exp if provided, and then walks up the execution tree until it hits a Catch expression with a matching tag, or until it hits the top level. In the latter case, program execution will be terminated.

'Catch'
Catch([catch_tag, handler_exp?], body_exp_1, body_exp_2, body_exp_n)

evaluates body_exp_1..n in sequence (like a Seq()). In the absence of an exception, evaluation proceeds exactly as for Seq(), with the value of <body_exp_n> being returned.

Exception processing: If an exception with throw_tag == catch_tag (under the host language's '==' equivalence relation) is thrown during the execution of the body expressions, the pending expression will be aborted. If no handler_exp is provided, Catch returns the value <throw_exp>. Otherwise, handler_exp will be evaluated. <handler_exp> is expected to be an Oni function. Next <handler_exp>(<throw_exp>) will be executed, and the result returned.

Examples:

Catch(['done'], Throw('done')) -> returns 'undefined'

Catch(['done'], Throw('done', 'The quick brown fox'))

    -> returns 'The quick brown fox'

Catch(['done',
       Lambda([x],
              SLift(function(s) {
                      return s.replace('quick', 'lazy');
                    })(Get('x')))
      ],
      Throw('done', 'The quick brown fox'))

    -> returns 'The lazy brown fox'

Other host language objects can be used as tags too:

var my_tag = {};
Catch([my_tag], ... Throw(my_tag) ...)

This is useful to prevent accidental capture of tags.


Function Application

'Apply': Function application
Apply(fexp, aexp_1, aexp_2, ..., aexp_n)

evaluates all of its subexpressions fexp, aexp_1..n concurrently. fexp is expected to yield an Oni function. Once all subexpressions have returned, the function <fexp> will be called with the argument list <aexp_1>..<aexp_n>.

If a pending Apply expression is aborted, any currently pending subexpressions or the pending function call will be aborted, too.

Example:

Let( { f : SLift(function(mes) { print(mes+'\n'); }) },
     Apply(Get('f'), "Hello, world"))

In the JavaScript version of Oni, Apply is obsolete since it can always be expressed as direct function call (see below).

Direct function call

In the JavaScript version of Oni, direct JavaScript function calls can be used synonymously with Apply.

I.e., the expression

fexp(aexp_1, aexp_2, ..., aexp_n)

is equivalent to

Apply(fexp, aexp_1, aexp_2, ..., aexp_n)

Example (equivalent to the example in the 'Apply' section above):

Let( { f : SLift(function(mes) { print(mes+'\n'); }) },
     Get('f')("Hello, world"))

Functional Abstraction

'Lambda': Locally scoped functions
Lambda([varname_1, varname_2, ..., varname_n],
       body_exp_1, body_exp_2, ..., body_exp_n)

defines an Oni function that, when applied to an argument array (see section on Function Application), binds varname_1..n to the corresponding argument values and executes body_exp_1..n sequentially (as in a Seq()). Finally <body_exp_n> is returned.

When a Lambda function is executed, the body expressions will be evaluated in the environment at the point of definition (i.e. Lambda() creates a closure):

Let({ x : 1, f : Lambda([], Get('x')) },
    Let({ x : 2},
        Get('f')()))

evaluates to 1, not 2.

'Defun': Globally scoped function
Defun([varname_1, varname_2, ..., varname_n],
      body_exp_1, body_exp_2, ..., body_exp_n)

is equivalent to Lambda(...) with the exception that it creates a top-level function that executes in a new empty top-level environment.

E.g. the code

Let({ x : 1, f : Defun([], Get('x')) },
    Let({ x : 2},
        Get('f')()))

evaluates to 'undefined'.

See also section on Function Application.


Function Lifting

'SLift': Synchronous Lift
SLift(sync_f)

returns an Oni function for the given synchronous host language construct sync_f.

In JavaScript, sync_f is expected to be a non-blocking function. If on invocation sync_f throws a JavaScript exception, this will be converted into an Oni exception with tag "error". Otherwise, the return value of sync_f will be returned.

Example:

SLift(function(x,y) { return x+y; })

This returns a function which can be called using Apply or by using direct function application:

var Add = SLift(function(x,y) { return x+y; });

var Expr1 = Print(Apply(Add, 1, 2));

var Expr2 = Print(Add(1,2));

Here the two Oni programs Expr1 and Expr2 are equivalent.

'ALift': Asynchronous Lift
ALift(async_f)

returns an Oni function for the given asynchronous host language construct async_f.

In JavaScript, async_f is expected to be a function with the signature:

abort_f async_f(cont, arg_1, arg_2, ..., arg_n);
  • async_f must not block, but immediately return an abort function abort_f (can be null).

  • async_f must asynchronously call cont exactly 0 or 1 times, passing in a return value structured in the following way:

    [true, return_value] : success, returning 'return_value'
    [false, err_string]  : failure; oni will throw an exception
                            with tag "error" and value 'err_string'.
    
  • If Oni calls abort_f before async_f has called cont, any subsequent call to cont will be ignored. abort_f will be called exactly 0 or 1 times. abort_f will not be called after async_f has called cont.

  • async_f will be executed with a this object set to the current environment.


Bindings

'Let'
Let(bindings_list, body_exp_1, body_exp_2, ..., body_exp_n)

creates a new environment E_new which inherits the bindings of the parent environment E_parent.

In the JavaScript implementation, bindings_list is of the form:

{ binding_name_1 : binding_exp_1,
  binding_name_2 : binding_exp_2,
  ...
  binding_name_n : binding_exp_n
}

where binding_name_1..n are identifiers or strings, and binding_exp_1..n are Oni expressions, functions, or primitive expressions.

Evaluation of the Let expression proceeds by evaluating binding_exp_1..n concurrently. The expressions will be evaluated in the new environment E_new. Any closures generated in the expressions will thus have access to the bindings introduced by the Let expression, allowing for (mutual) recursive definitions, e.g.:

Let({ "even?" : Lambda(['n'], If(IsZero(Get('n')),
                              true,
                              Apply(Get('odd?'), Sub1(Get('n'))))),
      "odd?"  : Lambda(['n'], If(IsZero(Get('n')),
                              false,
                              Apply(Get('even?'), Sub1(Get('n')))))
    },
    ... body ...
  )

where

 IsZero : SLift(function(x) { return x==0; })

and

 Sub1 : SLift(function(x) { return x-1; })

Once all expressions binding_exp_1..n have been evaluated, their values will be inserted into E_new. (XXX actually, at the moment values are inserted as the individual expressions return. This leads to a weak race condition if bindings from E_new are retrieved as part of the evaluation of the binding expressions. The correct behaviour would be for those retrievals to yield an 'undefined' value.)

Once the bindings have been inserted into E_new, the expressions body_exp_1..n will be evaluated sequentially (just like in a Seq()) in the new environment E_new. Finally, <body_exp_n> will be returned.

If a pending Let expression is aborted, any currently pending subexpressions will be aborted, too.

'Get'
Get(varname)

retrieves the value of the binding named varname from the current environment. In the JavaScript implementation, varname must resolve to a string.

If the environment doesn't contain a binding for the given varname, undefined will be returned.


Relational Operators, Predicates

'Equals'

Equality test.

The Oni function call

Equals(exp_a, exp_b)

returns the truth value of <exp_a> == <exp_b>, under the host language's equality relation ==.

'NEquals'

Inequality test.

The Oni function call

NEquals(exp_a, exp_b)

returns the truth value of <exp_a> != <exp_b>, under the host language's inequality relation !=.

'IsNull'

Test for 'null' or the empty list.

The Oni function call

IsNull(exp)

returns true if <exp> == null (where == and null are the host language's equality relation and the host language's 'null' value) or <exp> is an empty list (i.e. <exp> is a list and Length(<exp>) equals 0). Otherwise returns false.


Logical Operators

'Not'

Logical 'Not'.

The Oni function call

Not(exp)

returns !<exp>, where ! is the host language's logical 'Not' operator.


Mathematical Operators

'Add', 'Sub', 'Mul', 'Div'

Two-argument functions corresponding to the host language's floating point addition, subtraction, multiplication and division operators.

E.g. in the Javascript Oni version:

Add === SLift(function(a,b) { return a+b; })
'Random'

The Oni function call

Random(ceil_exp)

returns a pseudo-random number in the range [0,<ceil_exp>[, i.e. ranging from 0 (inclusive) up to but not including <ceil_exp>.


Timing

'Sleep'

The Oni function call

Sleep(duration_exp)

waits for <duration_exp> milliseconds before returning.

'Delay'
Delay(duration_exp, seq_exp_1, seq_exp_2, ..., seq_exp_n)

evaluates duration_exp and waits with further processing for <duration_exp> milliseconds. Then, if no seq_exp's are specified, it returns null. Otherwise seq_exp_1..n will be evaluated sequentially (as in a Seq()), and <seq_exp_n> will be returned.

'Time'
Time(exp_1, exp_2, ..., exp_n)

evaluates exp_1..n sequentially (as in a Seq()) and returns List(<exp_n>, T_elapsed), where T_elapsed is the (wall clock) time elapsed during the execution of exp_1..n.


List Structures

Oni contains Lisp/Scheme-inspired primitives for creating & manipulating immutable list structures.

In the Oni JavaScript version, lists are implemented as JavaScript arrays. Wherever a function takes a list as an argument, a literal JavaScript array will also be accepted.

E.g.

Car( List(1, 2, 3, 'a', 'b', 'c') )

is equivalent to

Car( [1, 2, 3, 'a', 'b', 'c'] )
'List': Create a list from individual elements

The Oni function call

List(elem_exp_1, elem_exp_2, ..., elem_exp_n)

returns a new list with elements <elem_exp_1>, <elem_exp_2>, ..., <elem_exp_n>. <elem_exp_1> forms the head of the list, and <elem_exp_2..n> the successive tails.

'Cons': Construct a list from an element and an existing list

The Oni function call

Cons(elem_exp, list_exp)

returns a new list with head <elem_exp> and tail <list_exp>.

'Car': Access the head of a list

The Oni function call

Car(list_exp)

returns the head of the (non-empty) list <list_exp>.

'Cdr': Access the tail of a list

The Oni function call

Cdr(list_exp)

returns the tail of the (non-empty) list <list_exp>.

'ListRef': Access a member of a list

The Oni function call

ListRef(list_exp, index_exp)

returns the <index_exp>'s element of <list_exp> (counting from 0).

'ListTail'

The Oni function call

ListTail(list_exp, index_exp)

returns the <index_exp>'s tail of <list_exp> (counting from 0; ListTail(.,0) returns the list itself).

Example:

ListTail(List('a','b','c','d'), 2)

returns the list List('c','d').

'Member': Test if an object is a member of a list

The Oni function call

Member(obj_exp, list_exp)

tests subsequent members of the list <list_exp> for equality (Equals(.,.)) against <obj_exp>. Returns the tail of <list_exp> headed by the first element that is equal to <obj_exp>, or false if no such element exists.

'Remove': Removes elements from a list

The Oni function call

Remove(obj_exp, list_exp)

returns a copy of <list_exp> with all elements equal (under Equals(.,.)) to <obj_exp> removed.

'Cadr', 'Caddr', 'Cadddr'

Shorthands for accessing list elements:

Cadr(l)   == ListRef(l, 1)
Caddr(l)  == ListRef(l, 2)
Cadddr(l) == ListRef(l, 3)
'GetCar'

GetCar(varname) retrieves the value v of the binding named varname and returns its Car. I.e. GetCar(varname) is a shorthand for Car(Get(varname)).

'GetCdr'

GetCdr(varname) retrieves the value v of the binding named varname and returns its Cdr. I.e. GetCdr(varname) is a shorthand for Cdr(Get(varname)).

'GetCadr', 'GetCaddr', 'GetCadddr'

Shorthands for accessing list elements of variables:

GetCadr(varname)   == ListRef(Get(varname), 1)
GetCaddr(varname)  == ListRef(Get(varname), 2)
GetCadddr(varname) == ListRef(Get(varname), 3)

Object Structures

Immutable JSON-like objects

'Obj': Construct an object

The Oni function call

Obj(pathexp_1, valexp_1, pathexp_2, valexp_2, ..., pathexp_n, valexp_n)

returns a new object o which will have its members identified by pathexp_1..n set to the corresponding values <valexp_1..n>.

pathexp_i can either be a list expression, or resolve to a host language string or number.

In the latter case, <pathexp_i> directly identifies a member on o.

If <pathexp_i> is a list (i.e. a JavaScript array in the Oni JavaScript version), the elements of the list, ListRef(<pathexp_i>, 0) to ListRef(<pathexp_i>, Length(<pathexp_i>) - 1), describe a path of nested members which will be created on o (as required), with the final nested member, ListRef(<pathexp_i>, Length(<pathexp_i>) - 1), set to <valexp_i>.

It is a runtime error if two or more of the pathexps resolve to the same member on o.

In the JavaScript Oni version, objects returned by Obj(...) are conventional JavaScript JSON-like objects.

Example:

Obj("foo", "bar",
    ["baz", 0], 1,
    ["baz", 1], "x",
    ["xxx", "yyy"], [1,2,3])

evaluates to the JSON object

{
  foo : "bar",
  baz : { 0 : 1, 1 :"x" },
  xxx : { yyy : [1,2,3] }
}
'ObjMem': Access (nested) members on an object

The function call

ObjMem(obj_exp, mem_exp_1, mem_exp_2, ..., mem_exp_n)

returns the value of the (nested) member identified by the path <List(mem_exp_1, mem_exp_2, ... mem_exp_n)> on the JSON object <obj_exp>.

Example:

ObjMem(Obj(["foo", "bar"], 42), "foo", "bar")

evaluates to 42.

Since in the JavaScript Oni version, Oni objects are conventional JavaScript JSON-like objects, ObjMem can be used to navigate JavaScript objects as well:

ObjMem({foo: {bar: 42}}, "foo", "bar")

evaluates to 42.


Protected Expressions

'Complete': Protect an expression from being aborted
Complete(exp_1, exp_2, ..., exp_n)

evaluates exp_1..n sequentially and returns <exp_n> (as in a Seq()).

Once evaluation has started, aborting the Complete() expression has no effect on the execution of exp_1..n; evaluation will proceed as normal, with the result <exp_n> discarded. The execution of the outer expression which aborted the Complete() expression will block until evaluation of the complete expression is finished.

E.g. the expression

Print(Alt("foo", Complete(Delay(1000, "bar"))))

will print "foo" only after 1 second, despite the fact that the Complete expression is aborted (almost) instantly by the Alt.

Exceptions thrown from within an aborted Complete expression will be silently caught, and control will return to the outer expression.

E.g. the expression

Print(Alt("foo", Complete(Timeout(500), Throw('error'), Timeout(500))))

will print "foo" after 0.5 seconds.

'Bracket'
Bracket(acquire_exp, use_exp, release_exp)

begins by evaluating acquire_exp in an unabortable way (as in Complete). If an exception is thrown during the evaluation, the execution of the Bracket expression is complete and control is returned to the outer expression (with the exception only being propagated to the outer expression, if the Bracket execution wasn't aborted in the meantime).

If acquire_exp completes without throwing an exception, but the Bracket expression has been aborted in the meantime, release_exp will be evaluated next, in an unabortable way (as in Complete).

Otherwise, if the Bracket expression hasn't been aborted, use_exp will be evaluated. If the Bracket expression is aborted during this phase, release_exp will be evaluated, in an unabortable way (as in Complete).

If use_exp returns normally, release_exp will be evaluated (again in an unabortable way) and the result <use_exp> will be returned to the outer expression.


Miscellaneous

'Quote'
Quote(datum)

yields datum as its value. datum can be any valid expression in the host language, i.e. it can be a primitive expression, an Oni expression or an Oni function.

Quote(datum) effectively prevents datum from being evaluated. This is e.g. useful to pass literal values to Oni expressions. Note that in the JavaScript implementation of Oni, primitive expressions (e.g. JavaScript literals) are self-quoting. E.g. the following two expressions are equivalent:

Print("Hello, world")

Print(Quote("Hello, world"))
'Nop'

The Oni function call

Nop()

returns null.

'Stop'

The Oni function call

Stop()

never returns.


Primitive Expressions

Host-language expressions other than expressions resolving to Oni expressions or Oni functions (i.e. p_exp's - primitive expressions) are self-quoting when appearing as arguments to Oni operators or Oni function applications.

I.e. an expression like Seq("foo") is equivalent to Seq(Quote("foo")).


Atomic Variables

Mutable variables which can be read/written atomically from concurrently executing Oni expressions: invocations of Read, Write and Transform are serialized with respect to each other.

'AtomicVar.Create'

The Oni function call

AtomicVar.Create(val_exp)

returns a new atomic variable object, set to the value <val_exp>.

'AtomicVar.Read'

The Oni function call

AtomicVar.Read(var_exp)

returns the current value of atomic variable <var_exp>.

'AtomicVar.Write'

The Oni function call

AtomicVar.Write(var_exp, val_exp)

sets the atomic variable <var_exp> to <val_exp>. The return value is undefined.

'AtomicVar.Transform'

The Oni function call

AtomicVar.Transform(var_exp, xform_exp)

calls <xform_exp>, which must be a host-language function of one variable, passing in the current value of the atomic variable <var_exp>, and sets the value of <var_exp> to the return value of the function call. It then returns the new value.

The transformer function <xform_exp> must not access the atomic variable <var_exp> indirectly (e.g. by Eval'ing some Oni expression that has access to the variable). XXX provide example of this.

Example:

function add1(x) { return x+1; }

Let( { x: AtomicVar.Create(0) },
     Par(AtomicVar.Transform(Get('x'), add1),
         AtomicVar.Transform(Get('x'), add1),
         AtomicVar.Transform(Get('x'), add1)),
     Print(AtomicVar.Read(Get('x'))) )

--> Prints '3' when evaluated