# Expressions

The body of transforms is specified using Flex's language of expressions that is syntactically similar to C++ and Java expressions. We will discuss Flex expressions informally here. For the complete grammar of Flex including its expression language, see Flex Complete Syntax.

## Literals​

Literals for built-in types are similar to literals in other languages. Integer literals in Flex can be used as values of any of the integral data types (`int8`, `int16`, ..., `uint8`, `uint16`, ...). Decimal literals can be used as values of `float32` and `float64`.

`flex`42                       // integral literal-42                      // integral literal1_000_000                // integral literal3.14                     // decimal literal6.022e23                 // decimal literal0xC0A86401               // integral literal (hexadecimal)0b0000111100001111       // integral literal (binary)"foo bar"                // string literaltrue                     // bit literalfalse                    // bit literal``

## Arithmetic and Logic​

Flex has arithmetic, logical, and comparison operators similar to other languages.

The arithmetic operators `+ - * / %` and comparison operators `> >= < <=` are defined over all numeric types.

`flex`(42: int32) + 8             // ~> 50: int32(42: int32) - 8             // ~> 34: int32(42: int32) * 8             // ~> 316: int32(42: int32) / 8             // ~> 5: int32(42: int32) % 8             // ~> 2: int32(40: int32) == 42           // ~> false(40: int32) != 42           // ~> true(40: int32) > 42            // ~> false(40: int32) >= 42           // ~> false(40: int32) < 42            // ~> true(40: int32) <= 42           // ~> true``

The binary logical operators `&& ||` are defined over `bit` as well as integral types (as bitwise operations). Unary negation `!` is defined over `bit` only.

`flex`true && false               // ~> falsetrue || false               // ~> true(5: uint8) || 3             // ~> 7: uint8(5: uint8) && 3             // ~> 1: uint8!true                       // ~> false``

Equality operators `== !=` are defined over all types. Inequality is just the negation of equality (i.e., `x != y` is equivalent to `!(x == y)`).

`flex`(42: int32)      == 42                            // ~> truefalse            == false                         // ~> true"foo"            == "foo"                         // ~> truePos{ x=0; y=0; } == Pos{ x=0; y=0; }              // ~> truePos{ x=0; y=0; } == Pos{ y=0; x=0; }              // ~> true(true, "foo")    == (true, "foo")                 // ~> true(42: int32)      == 43                            // ~> falsetrue             == false                         // ~> false"foo"            == "bar"                         // ~> falsePos{ x=0; y=1; } == Pos{ x=0; y=0; }              // ~> falsePos{ x=0; y=1; } == Pos{ y=0; x=1; }              // ~> false(true, "foo")    == (true, "bar")                 // ~> false``

For all binary operators, Flex requires that the left- and right-hand sides have the same type, and there is no implicit type coersion.

`flex`(42: int32) + (43: int16)                         // type-checking error(42: int32) < (43: int16)                         // type-checking error(42: int32) == (42.0: float64)                    // type-checking errortrue == "true"                                    // type-checking errortrue != (0: int32)                                // type-checking error(42: int8, "foo") == ("foo", 42: int8)            // type-checking error``

Although there is no implicit type conversion, Flex can infer the type of numeric literals as long as there is only a single type that would satisfy typing constraints. A type ascription (e.g., `: int32`) is usually enough information to satisfy the type checker when there would otherwise be ambiguity. For example, the types of the literals in `42 + 8 * 9` are ambiguous, making the expression invalid. However, `(42: int32) + 8 * 9` as well as `(42 + 8 * 9) : int32` are both acceptable.

## Casting​

Flex supports several ways to cast numeric values to other numeric types. `value_cast` is used when the type conversion from type `A` to `B` is guaranteed to succeed for all values of type `A` (e.g., `uint16` to `uint32`). If the cast may fail for some `A`, then `value_cast?` is used instead which returns an `Optional<B>`. For converting `float64` to `float32`, there is a special function `float64to32` that performs a lossy conversion.

`flex`value_cast<int32>(42 : int16)        // ~> 42 : int32value_cast<int32>(42: uint16)        // ~> 42 : int32value_cast<float64>(3.14 : float32)  // ~> 3.14 : float32value_cast?<int32>(42 : int16)       // ~> some(42 : int32)value_cast?<int8>(127 : int16)       // ~> some(127 : int8)value_cast?<int8>(128 : int16)       // ~> nonevalue_cast?<int8>(128 : uint8)       // ~> nonevalue_cast?<uint8>(-42 : int8)       // ~> nonefloat64to32(3.14)                    // ~> 3.14 : float32``

`value_cast?` is often used with the `getOrElse` function to provide an alterative value if the cast fails:

`flex`getOrElse(value_cast?<uint8>(42: int8), 0)      // ~> 42 : uint8getOrElse(value_cast?<uint8>(-42: int8), 0)     // ~> 0 : uint8``

Flex does not include built-in functions for casting to and from `bit` and `string` types. Since there are choices to be made about precisely how such conversions would be implemented, we leave it to the user to define them as helper functions. As an example:

`flex`function bit2uint8(b: bit) -> uint8 =  match (b) {    false => 0x00;    true  => 0xFF;    // true => 0x01;      // another possibility  };``

## Statements and Blocks​

Flex takes after functional languages in that computation is specified primarily with expressions rather than statements. However, Flex does include a few statement forms that are syntactically reminiscent of imperative languages.

Statements must appear in a block, an expression form composed of a sequence of statements and a final expression:

`flex`{  let pi: float64 = 3.14;  let radius: float64 = diameter / 2.0;  assert radius > 0;  3.14 * radius * radius;}``

The `let` statement binds the result of an expression to an identifier. Bound identifiers are in scope for subsequent statements and the final expression. They are not in scope outside the block.

`flex`let radius: float64 = diameter / 2.0;``

If the result of the expression is a tuple, then the contents of the tuple can be unpacked and bound to two or more identifiers.

`flex`let (x, y) = rotatePoint2D((3.0, 4.0), 45.0);``

Note that since Flex does not have mutable state, binding an expression to an identifier is not the same as assigning a value to a variable in C++. The value bound to an identifier cannot be "changed" during execution. Flex also does not support shadowing: an identifier `x` cannot be introduced if another identifier named `x` is already in scope.

The `assert` statement causes the surrounding block expression to fail if a condition is not satisfied.

`flex`assert radius > 0;``

The result of evaluating a block expression is the result of the final expression, taking into account identifiers bound by `let` statements and failures prescribed by `assert` statements.

The bodies of functions and transforms are usually block expressions because `let` bindings provide a convenient way to break down complex calculations. If the body is a block expression, the `=` sign that would otherwise precede the body may be omitted.

`flex`function eulerToQuaternion(e: Euler) -> Quaternion {  let t0 = cos(e.Yaw * 0.5);  let t1 = sin(e.Yaw * 0.5);  let t2 = cos(e.Roll * 0.5);  let t3 = sin(e.Roll * 0.5);  let t4 = cos(e.Pitch * 0.5);  let t5 = sin(e.Pitch * 0.5);  Quaternion {    w = t2 * t4 * t0 + t3 * t5 * t1;    x = t3 * t4 * t0 - t2 * t5 * t1;    y = t2 * t5 * t0 + t3 * t4 * t1;    z = t2 * t4 * t1 - t3 * t5 * t0;  };}``

## Conditionals​

Flex supports two kinds of conditional evaluation: `if-else` expressions and `match` expressions.

The `if-else` expression is comparable to the ternary operator `? :` in C++ and Java, though it syntactically resembles the `if-else` blocks from those languages. Note that in Flex, `if-else` is not a block that contains statements. It is an expression that returns either the value of the `then` branch or the value of the `else` branch. Because of this the types of the then and else branches must be compatible.

The syntax `if-else` can be written using `then` as a separating token or with the condition wrapped in parentheses:

`flex`const x: int32 = 42;if x < 100 then x else x - 100;         // ~> 42: int32if x < 20 then x else x - 20;           // ~> 22: int32if x > 100 then x - 100else if x > 10 then x - 10else x                                  // ~> 32: int32if (x > 100) {  x - 100;} else if (x > 10) {  x - 10;} else {  x;}                                       // ~> 22: int32``

Flex also supports conditional evaluation based on pattern matching (see Pattern Matching).

## Tuples and Arrays​

Aside from user-defined types, Flex has built-in support for two kinds of aggregate data structure: tuples and arrays (e.g., heterogeneous and homogeneous lists).

A tuple is a list of two or more values, possibly of different types. A tuple is introduced by listing its elements inside parentheses, for example `(42: int8, "foo", true)`. Tuples are usually used by unpacking the contents using a `let` statement:

`flex`let (x, y, z) = (1: int32, 2: int32, 3: int32);``

An array is a list of zero or more values of the same type. An array is introduced by listing the elements inside square brackets (e.g., `["one", "two", "three"]`). The primitive operations on arrays are access `[]`, concatenation `++`, `length`, and `last`.

`flex`const squareNums: uint8[] = [1, 4, 9, 16];squareNums[0 : int32]                          // ~> 1 : uint8squareNums[4 : int32]                          // failssquareNums[0 : int32 .. 2 : int32]             // ~> [1, 4, 9] : uint8[]squareNums ++ [25, 36]                         // ~> [1, 4, 9, 16, 25, 36]: uint8[]length(squareNums)                             // ~> 4: uint32last(squareNums)                               // ~> 16: uint8last([]: uint8[])                              // fails``

Arrays can also be iterated over using an array comprehension. For example, here is an array comprehension that adds one to each element of the array `squareNums`:

`flex`[x + 1 for x in squareNums]                    // ~> [2, 5, 10, 17]: uint8[]``

The values can be filtered using an `if` clause:

`flex`[x + 1 for x in squareNums if x%2==0]          // ~> [5, 17]: uint8[]``

Sometimes the element-wise operation needs to depend on the results of previous operations. Information can be carried from index to index using `scan` such as in this example to compute an array of cumulative sums:

`flex`const evens: uint8[] = [2, 4, 6, 8];[cumulSum for x in evens scan cumulSum: uint8 = 0 in cumulSum + x]                                               // ~> [2, 6, 12, 20]: uint8[]``

You can also zip arrays together by separating multiple `for` clauses with the `zip` keyword:

`flex`[x+y for x in squareNums zip for y in evens]   // ~> [3, 8, 15, 24]: uint8[]``

## Optionals​

Flex also has built in support for optional values. A value of type `Optional<A>` has one of two forms: `some(a)` where `a` is a value of type `A`, or `none`. An `Optional<A>` value is like a box that might contain a value of type `A`, or it might contain nothing. Optionals in Flex are often used with pattern matching.

`flex`function safeDivide(num : float32, den : float32) -> Optional<float32> {  if den == 0.0 then none else some(num / den); }const safeDivideResult : Optional<float32> = safeDivide(2.0,3.0); match(safeDivideResult) {  some(result) => result;  none => 0.0;}// ~> 0.6666667: float32``