On this page:
4.1 Variable expressions
4.2 Literal expressions
True
False
None
4.3 Functions and application expressions
lambda
λ
4.4 Vectors and indexing expressions
4.5 Structs and projection expressions
4.6 Operator expressions
**
not
~
*
/
%
+
-
<<
>>
&
^
\|
==
!=
is
|is not|
<
<=
>
>=
and
or
7.4

4 Expression forms

4.1 Variable expressions

expr

var_name

The value of a variable, which must be a function parameter, bound with let, or defined with def. For example,

let x = 5
println('%p', x)

prints “5”.

Lexically, a variable is a letter or underscore, followed by zero or more letters, underscores, or digits, optionally ending in a question mark or exclamation point.

4.2 Literal expressions

expr

number

A numeric literal.

expr

string

A string literal.

expr

True 

The true Boolean value.

expr

False 

The false Boolean value.

This is one of only two values that are not considered truthy for the purpose of conditionals. The other non-truthy value is None.

expr

None 

A special value for representing missing data. This is also the result of functions that are called just for their side effects, such as print.

This is one of only two values that are not considered truthy for the purpose of conditionals. The other non-truthy value is False.

4.3 Functions and application expressions

expr

expr0⟩(⟨expr1⟩, ..., ⟨exprk⟩)

Evaluates all the expressions; ⟨expr0⟩ must evaluate to a procedure. Then applies the result of ⟨expr0⟩ with the results of the other expressions as arguments.

For example,

fact(5)

calls the function fact with argument 5, and

ack(5 + 1, 5 + 2)

calls the function ack with arguments 6 and 7.

Note that method calls are just object method lookup combined with procedure applcation. That is, when you write

q.enqueue(5)

that means lookup the enqueue method in q, and then apply the result to 5.

expr

lambda var_name1, ..., var_namek: ⟨simple

λ var_name1, ..., var_namek: ⟨simple

Creates an anonymous function with parameters var_name1, ..., var_namek and body ⟨simple⟩. For example, the function to add twice its first argument to its second argument can be written

lambda x, y: 2 * x + y

4.4 Vectors and indexing expressions

expr

expr1⟩[⟨expr2⟩]

Expression ⟨expr1⟩ must evaluate to a vector or string o; ⟨expr2⟩ must evaluate to an integer n between 0 and o.len() - 1. Then this returns the nth element of vector o or the nth character of string o.

expr

[ ⟨expr1⟩, ..., ⟨exprk⟩ ]

Creates a new vector of length k whose values are the values of the expressions.

For example:

let v = [ 1, 2, 3, 4, 5 ]

expr

[ ⟨exprinit⟩; ⟨exprsize⟩ ]

Constructs a new vector whose length is the value of ⟨exprsize⟩, filled with the value of ⟨exprinit⟩. That is,

[ 0; 5 ]

means the same thing as

[ 0, 0, 0, 0, 0 ]

Note that ⟨exprinit⟩ is not re-evaluated to produce each element, so an expression like [[0; 5]; 5] produces a vector that contains the same vector five times, not five different subvectors.

expr

[ ⟨exprelem⟩ for var_name in ⟨expriter⟩ ]

[ ⟨exprelem⟩ for var_name1, var_name2 in ⟨expriter⟩ ]

Vector comprehensions: produces a vector of the values of ⟨exprelem⟩ while iterating the variable(s) over ⟨expriter⟩. In particular, ⟨expriter⟩ must be a vector v, a string s, or a natural number n; in which case the iterated-over values are the elements of v, the characters of s, or counting from 0 to n - 1, respectively. If one variable var_name is provided, it takes on those values. If two are provided, then var_name2 takes on those values, while var_name1 takes on the indices counting from 0 upward.

For example,

[ 10 * n for n in [ 5, 4, 3, 2, 1 ] ]

evaluates to

[ 50, 40, 30, 20, 10 ]

And

[ 10 * n + i for i, n in [ 5, 4, 3, 2, 1 ] ]

evaluates to

[ 50, 41, 32, 23, 14 ]

expr

[ ⟨exprelem⟩ for var_name in ⟨expriter⟩ if ⟨exprcond⟩ ]

[ ⟨exprelem⟩ for var_name1, var_name2 in ⟨expriter⟩ if ⟨exprcond⟩ ]

If the optional ⟨exprcond⟩ is provided, only elements for which ⟨exprcond⟩ is non-false are included. That is, the variable(s) take on each of their values, then ⟨exprcond⟩ is evaluated in the scope of the variable(s). If it’s non-false then ⟨exprelem⟩ is evaluated and included in the resulting vector.

For example,

[ 10 * n for n in [ 5, 4, 3, 2, 1 ] if odd?(n) ]

evaluates to

[ 50, 30, 10 ]

4.5 Structs and projection expressions

expr

expr⟩.prop_name

Expression ⟨expr⟩ must evaluate to struct value that has field prop_name or an object value that has method prop_name; then this expression evaluates to the value of that field of the struct or that method of the object.

expr

struct_name { field_name1: ⟨expr1⟩, ..., field_namek: ⟨exprk⟩ }

Constructs a struct with the given name and the values of the given expressions for its fields. The struct must have been declared with those fields using struct.

If a variable with the same name as a field is in scope, omitting the field value will use that variable:

struct Foo:
    let bar
    let baz
 
let bar = 4
let baz = 5
 
assert Foo { bar, baz: 9 } == Foo(4, 9)

4.6 Operator expressions

Operators are described in order from tighest to loosest precedence.

expr

expr1**expr2

Raises the value of ⟨expr1⟩ to the power of the value of ⟨expr2⟩, both of which must be numbers.

The ** operator is right-associative.

expr

notexpr

~expr

-⟨expr

+⟨expr

Logical negation, bitwise negation, numerical negation, and numerical identity.

notexpr⟩ evaluates ⟨expr⟩, then returns True if the result was False or None, and False for any other result.

~expr⟩ requires that ⟨expr⟩ evalutes to an integer or Boolean; it flips every bit of the number, or negates the Boolean.

-expr⟩ and +expr⟩ require that ⟨expr⟩ evaluates to a number. Then - negates the number, and + returns it unchanged.

expr

expr1*expr2

expr1/expr2

expr1%expr2

Multiplies, divides, or modulos the values of the expressions, respectively.

expr

expr1+expr2

Addition:

Anything else is an error.

expr

expr1-expr2

Subtracts two numbers.

expr

expr1<<expr2

expr1>>expr2

Left and right bitwise shift.

expr

expr1&expr2

Bitwise and for integers; logical and for Booleans.

expr

expr1^expr2

Bitwise xor for integers; logical xor for Booleans.

expr

expr1\|expr2

Bitwise or for integers; logical or for Booleans. (Not written with the backslash.)

expr

expr1==expr2

expr1!=expr2

expr1isexpr2

expr1|is not|expr2

expr1<expr2

expr1<=expr2

expr1>expr2

expr1>=expr2

Operator == is structural equality (except for classes that override it), and != is its negation. Operator is is physical equality, and |is not| (not written with the vertical bars) is its negation. To understand the difference, suppose that we create two different vectors with the same contents. Those vectors are structurally equal but not physically equal.

Operators <, <=, >, and >= are the standard inequalities for numbers and characters, and compare strings in lexicographic order.

expr

expr1andexpr2

Short-circuiting logical and. First evaluates ⟨expr1⟩; if the result is False or None then the whole conjunction is False; otherwise, the result of the conjunction is the result of ⟨expr2⟩.

expr

expr1orexpr2

Short-circuiting logical or. First evaluates ⟨expr1⟩; if the result is non-false then the whole disjunction has that result; otherwise the result of the disjunction is the result of ⟨expr2⟩.

expr

exprthen⟩ if ⟨exprcond⟩ else ⟨exprelse

The ternary expression first evaluates the condition ⟨exprcond⟩. If non-false, evaluates ⟨exprthen⟩ for its value; otherwise, evaluates ⟨exprelse⟩ for its value.

For example:

def parent(link):
    link.parent if rbn?(link) else None