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:
-
Typein arrayType[]:int[] arr = [1, 2, 3, 4, 5] assert(5 in arr) -
Keyin map[Key]Value:[char]int map = ['a': 1, 'b': 2, 'c': 3] assert('a' in map) -
char/strinstr:str string = "Hello world" assert('H' in string) assert("Hello" in string) -
int/uintin ranges:assert(1 in 1:5) // int assert(1u in 1u:5u) // uint