Syntax desugaring

aep edited this page Jan 27, 2012 · 23 revisions

A large amount of Clay syntax, including almost every operator, is actually translated into plain function calls, or sequences of function calls, by the Clay parser. This page aims to document these transformations, especially the ones that are useful to overload.

In the following tables, a, b, c represent arbitrary single-value subexpressions. ..a, ..b, ..c represent zero or more arbitrary subexpressions yielding zero or more values. foo, bar, bas represent literal identifiers. a;, b;, c; represent an arbitrary statement (including an arbitrary { ... } block). 0, 1, 2 represent literal integers. In the "equivalent" column, _x, _y, _z represent nonaccessible temporary variable names.

All referenced function names are looked up in the prelude module unless otherwise indicated. Note that if, for example, you create a new function named add in a different module, that new function will not affect the behavior of the + operator:

// This won't overload +
add(x: Foo, y: Foo) = addFoo(x, y);

When overloading operators, you must use the overload keyword:

overload add(x: Foo, y: Foo) = addFoo(x, y);

Operator expressions

Operator expression Equivalent function expression Notes
+a plus(a)
-a minus(a)
&a __primitives__.addressOf(a) (1)
*a (see Variant Dispatch) (1)
a[..b] index(a, ..b)
a(..b) call(a, ..b) (2) fieldRef(a, #foo)
a.0 staticIndex(a, static 0)
a^ dereference(a)
a * b multiply(a, b)
a / b divide(a, b)
a % b remainder(a, b)
a + b add(a, b)
a - b subtract(a, b)
a < b lesser?(a, b)
a <= b lesserEquals?(a, b)
a > b greater?(a, b)
a >= b greaterEquals?(a, b)
a == b equals?(a, b)
a != b notEquals?(a, b)
if (a) b else c ifExpression(a, b, c) (3)
[..a] tupleLiteral(..a)
foo: a tupleLiteral(#foo, a)


  1. Not overloadable.
  2. Calls to statically named functions do not desugar into call forms. The function is invoked directly.
  3. Only the expression form of if desugars into an ifExpression call. if statements do not desugar into a function form.

Assignment operators

Assignment in Clay is not an expression; however, the assignment operators still desugar into function calls:

Assignment statement Equivalent function statement Notes
a = b; assign(a, b); (1)
a <-- b; (see Initialization)
a <x>= b; updateAssign(<op>, a, b); (2)
a.b = c; fieldRefAssign(a, #"b", c);
a[..b] = c; indexAssign(a, ..b, c);
a.0 = c; staticIndexAssign(a, static 0, c);
a.b <x>= c; fieldRefUpdateAssign(<op>, a, #"b", c); (2)
a[..b] <x>= c; indexUpdateAssign(<op>, a, ..b, c); (2)
a.0 <x>= c; staticIndexUpdateAssign(<op>, a, static 0, c); (2)


  1. var a = b; behaves like <-- instead of calling assign. See Initialization. forward a = b; for rvalue b behaves like var a = move(b);. ref a = b;, forward a = b; for lvalue b, and alias a = b; merely bind a as a reference or alias and have no function equivalents.
  2. <x> can be one of the binary operators +, -, *, /, or %, which respectively map to the <op> argument add, subtract, multiply, divide, and remainder in the desugared updateAssign forms. For example, a += b; desugars to updateAssign(add, a, b);, and a.b %= c; desugars to fieldRefUpdateAssign(remainder, a, #"b", c);.

Control Flow

Control flow statement Equivalent code
for (a in b)
    forward _x = b;
    var _y = iterator(_x);
    while (hasNext?(_y)) {
        ref a = next(_y);
..for (a in ..b)
    forward a = <first element of b>;
    forward a = <second element of b>;
/* ... */
throw a;


The initializing assignment operator a <-- b; initializes the value a as if it is uninitialized memory. The exact behavior of <-- depends on whether the expression b returns by value or by reference, or is a forwarded lvalue or rvalue:

Conditions Expression Function call
f is a function or operator that returns by value a <-- f(..b); f(..b)
b is one of the following:
  • a simple variable or non-forward argument
  • an expression that returns by ref
  • a forwarded lvalue argument
a <-- b; copy(b)
b is a forwarded rvalue argument a <-- b; move(b)

Regardless of whether f, copy, or move is invoked at the top level, the function's return value is written directly into a. Note that both copy and move are return-by-value functions, so copying or moving can be explicitly enacted with a <-- copy(b); or a <-- move(b);. In the multiple-value form ..a <-- ..b;, each right-hand expression is considered independently with the above rules to determine whether it is directly written, copyed, or moved into its corresponding destination.

The variable definition form var a = b; behaves like <--; you could think of it as desugaring to var a; a <-- b; (although var a; is invalid). ref a = b; with rvalue b behaves like var a = move(b);.

Variant Dispatch

(to be written)

"With" expressions

A "with" expression creates a continuation from the current block and passes it as lambda to another function.

{ with a,b = c(d); f(b); g(a); }

is the short form of

{ return c(d, (a,b) -> { f(b); g(a); } ); }