Skip to content

Operators

All operators (except the pipe operator) will require its arguments to be of the same type. So, it will not be possible to add an int to a rat without first converting one or the other to the respective type.

Logical

// and
true and true // true
true and false // false
false and false // false
// or
true or true // true
true or false // true
false or false // false
// not
not true // false
not false // true

Concatenation

Because strings are arrays of characters, the concatenation operator is the same for both.

[a, b, c] <> [d, e, f] == [a, b, c, d, e, f]
"Hello" <> " " <> "world" == "Hello world"

You can also use the concatenation operator between maps, provided that they are of the same type:

[str]int monthsToNum1 = ["jan": 1, "feb": 2, "mar": 3]
[str]int monthsToNum2 = ["apr": 4, "may": 5, "jun": 6]
[str]int monthsToNum3 = ["jul": 7, "aug": 8, "sep": 9]
[str]int monthsToNum4 = ["oct": 10, "nov": 11, "dec": 12]

[str]int monthsToNum = monthsToNum1 <> monthsToNum2 <> monthsToNum3 <> monthsToNum4
// ["jan": 1, "feb": 2, "mar": 3, "apr": 4, ... , "dec": 12]

[str]rat differentType = ["new": 13.0]
monthsToNum <> differentType // error: cannot concatenate maps of different types

Arithmetic

// addition
1 + 2 == 3
// subtraction
2 - 1 == 1
// exponent
2 ** 6 == 64
// modulo
10 % 4 == 2
// division
5.0 / 2.0 == 2.5
5 / 2 == 2
// multiplication
3 * 2

Comparisons

// equality
1 == 1
// inequality
1 != 2
// less than
1 < 2
// greater than
1 > 0
// less than or equal to
5 <= 6
5 <= 5
// greater than or equal to
7 >= 4
7 >= 7

Bit arithmetic

Bit arithmetic will only be allowed for integers.

// bit shift left
1 << 4 == 16
// bit shift right
6 >> 1 == 3
// bitwise and
27 & 1 == 1
// bitwise or
8 | 1 == 9
// bitwise xor
12 ^ 1 == 13
// bitwise not
!6 == -7

Pipes

Similar to functional programming, we can use a |> pipe operator to pass values from one function to the next. This will automatically fill in the first value of the receiving function with the value being passed into it. For example, something like this:

fn square(int[] numbers) -> int[] {
  mut int[] numbers = numbers

  for i in numbers {
    numbers[i] = numbers[i] ** 2
  }

  return numbers
}

fn addToAll(int[] numbers, int adding) -> int[] {
  mut int[] numbers = numbers

  for i in numbers {
    numbers[i] += adding
  }

  return numbers
}

mut int[] numbers = [1, 2, 3, 4, 5]
numbers = square(numbers) // [1, 4, 9, 16, 25]
numbers = addToAll(numbers, 5) // [6, 10, 15, 21, 30]

can be turned into a pipeline, like so:

// Same function definitions as above

int[] numbers = [1, 2, 3, 4, 5]
                  |> square()
                  |> addToAll(5)

While this may seem pointless for a simple case like this, it becomes incredibly helpful for situations where a piece of data needs to be passed around a lot. For example:

// Some data to be transformed
DataStruct[] newData = data
                         |> map(someFn)
                         |> filter(someFilter)
                         |> sort(someSort)
                         |> take(10) // Take the first 10 values

Inclusion

The in keyword acts as the inclusion operator, used for checking if a value is included in a container value. You can check for:

  • Type in array Type[]:

    int[] arr = [1, 2, 3, 4, 5]
    assert(5 in arr)
    
  • Key in map [Key]Value:

    [char]int map = ['a': 1, 'b': 2, 'c': 3]
    assert('a' in map)
    
  • char/str in str:

    str string = "Hello world"
    assert('H' in string)
    assert("Hello" in string)
    
  • int/uint in ranges:

    assert(1 in 1:5) // int
    assert(1u in 1u:5u) // uint