# Types¶

## Simple types¶

### boolean¶

```val using_rell = true;
if (using_rell) print("Awesome!");
```

### integer¶

```val user_age : integer = 26;
```

`integer.MIN_VALUE` = minimum value (`-2^63`)

`integer.MAX_VALUE` = maximum value (`2^63-1`)

`integer(s: text, radix: integer = 10)` - parse a signed string representation of an integer, fail if invalid

`integer(decimal): integer` - converts a decimal to an integer, rounding towards 0 (5.99 becomes 5, -5.99 becomes -5), throws an exception if the resulting value is out of range

`integer.from_text(s: text, radix: integer = 10): integer` - same as `integer(text, integer)`

`integer.from_hex(text): integer` - parse an unsigned HEX representation

`.abs(): integer` - absolute value

`.max(integer): integer` - maximum of two values

`.max(decimal): decimal` - maximum of two values (converts this `integer` to `decimal`)

`.min(integer): integer` - minimum of two values

`.min(decimal): decimal` - minimum of two values (converts this `integer` to `decimal`)

`.to_text(radix: integer = 10)` - convert to a signed string representation

`.to_hex(): text` - convert to an unsigned HEX representation

`.sign(): integer` - returns `-1`, `0` or `1` depending on the sign

### decimal¶

Represent a real number.

```val approx_pi : decimal = 3.14159;
val scientific_value : decimal = 55.77e-5;
```

It is not a normal floating-point type found in many other languages (like `float` and `double` in C/C++/Java):

• `decimal` type is accurate when working with numbers within its range. All decimal numbers (results of decimal operations) are implicitly rounded to 20 decimal places. For instance, `decimal('1E-20')` returns a non-zero, while `decimal('1E-21')` returns a zero value.
• Numbers are stored in a decimal form, not in a binary form, so conversions to and from a string are lossless (except when rounding occurs if there are more than 20 digits after the point).
• Floating-point types allow to store much smaller numbers, like `1E-300`; `decimal` can only store `1E-20`, but not a smaller nonzero number.
• Operations on decimal numbers may be considerably slower than integer operations (at least 10 times slower for same integer numbers).
• Large decimal numbers may require a lot of space: ~0.41 bytes per decimal digit (~54KiB for 1E+131071) in memory and ~0.5 bytes per digit in a database.
• Internally, the type `java.lang.BigDecimal` is used in the interpreter, and `NUMERIC` in SQL.

In the code one can use decimal literals:

```123.456
0.123
.456
33E+10
55.77e-5
```

Such numbers have `decimal` type. Simple numbers without a decimal point and exponent, like 12345, have `integer` type.

`decimal.PRECISION: integer` = the maximum number of decimal digits in a `decimal` number (131072 + 20)

`decimal.SCALE: integer` = the maximum number of decimal digits after the decimal point (20)

`decimal.INT_DIGITS: integer` = the maximum number of decimal digits before the decimal point (131072)

`decimal.MIN_VALUE: decimal` = the smallest nonzero absolute value that can be accurately stored in a `decimal` (1E-20)

`decimal.MAX_VALUE: decimal` = the largest value that can be stored in a `decimal` (1E+131072 - 1)

`decimal(integer): decimal` - converts `integer` to `decimal`

`decimal(text): decimal` - converts a text representation of a number to `decimal`. Exponential notation is allowed. Rounds the number to 20 decimal places, if necessary. Throws an exception if the number is out of range or not a valid number.

`.abs(): decimal` - absolute value

`.ceil(): decimal` - ceiling value: rounds 1.0 to 1.0, 1.00001 to 2.0, -1.99999 to -1.0, etc.

`.floor(): decimal` - floor value: rounds 1.0 to 1.0, 1.9999 to 1.0, -1.0001 to -2.0, etc.

`.min(decimal): decimal` - minimum of two values

`.max(decimal): decimal` - maximum of two values

`.round(scale: integer = 0): decimal` - rounds to a specific number of decimal places, to a closer value. Example: `round(2.49)` = 2.0, `round(2.50)` = 3.0, `round(0.12345, 3)` = 0.123. Negative scales are allowed too: `round(12345, -3)` = 12000.

`.sign(): integer` - returns `-1`, `0` or `1` depending on the sign

`.to_integer(): integer` - converts a decimal to an integer, rounding towards 0 (5.99 becomes 5, -5.99 becomes -5), throws an exception if the resulting value is out of range

`.to_text(scientific: boolean = false): text`

### text¶

Textual value. Same as `string` type in some other languages.

```val placeholder = "Lorem ipsum donor sit amet";
print(placeholder.size());  // 26
print(placeholder.empty()); // false
```

`text.from_bytes(byte_array, ignore_invalid: boolean = false)` - if `ignore_invalid` is `false`, throws an exception when the byte array is not a valid UTF-8 encoded string, otherwise replaces invalid characters with a placeholder.

`.empty(): boolean`

`.size(): integer`

`.compare_to(text): integer` - as in Java (returns 0 if strings match and a positive or negative value if they don’t match)

`.starts_with(text): boolean`

`.ends_with(text): boolean`

`.contains(text): boolean` - `true` if contains the given substring

`.index_of(text, start: integer = 0): integer` - returns `-1` if substring is not found (as in Java)

`.last_index_of(text[, start: integer]): integer` - returns `-1` if substring is not found (as in Java)

`.sub(start: integer[, end: integer]): text` - get a substring (start-inclusive, end-exclusive)

`.replace(old: text, new: text)`

`.upper_case(): text`

`.lower_case(): text`

`.split(text): list<text>` - strictly split by a separator (not a regular expression)

`.trim(): text` - remove leading and trailing whitespace

`.matches(text): boolean` - `true` if matches a regular expression

`.to_bytes(): byte_array` - convert to a UTF-8 encoded byte array

`.char_at(integer): integer` - get a 16-bit code of a character

`.format(...)` - formats a string (as in Java):

• `'My name is <%s>'.format('Bob')` - returns `'My name is <Bob>'`

Most of these text functions can be used within at-expressions where they will be translated to their SQL equivalents.

Special operators:

• `+` : concatenation
• `[]` : character access (returns single-character `text`)

### byte_array¶

```val user_pubkey : byte_array = x"0373599a61cc6b3bc02a78c34313e1737ae9cfd56b9bb24360b437d469efdf3b15";
print(user_pubkey.to_base64()); //A3NZmmHMazvAKnjDQxPhc3rpz9Vrm7JDYLQ31Gnv3zsV
```

`byte_array(text)` - creates a `byte_array` from a HEX string, e.g. `'1234abcd'`, throws an exception if the string is not a valid HEX sequence

`byte_array.from_hex(text): byte_array` - same as `byte_array(text)`

`byte_array.from_base64(text): byte_array` - creates a `byte_array` from a Base64 string, throws an exception if the string is invalid

`byte_array.from_list(list<integer>): byte_array` - creates a `byte_array` from a list; values must be 0 - 255, otherwise an exception is thrown

`.empty(): boolean`

`.size(): integer`

`.sub(start: integer[, end: integer]): byte_array` - sub-array (start-inclusive, end-exclusive)

`.to_hex(): text` - returns a HEX representation of the byte array, e.g. `'1234abcd'`

`.to_base64(): text` - returns a Base64 representation of the byte array

`.to_list(): list<integer>` - list of values 0 - 255

`.sha256(): byte_array` - returns the sha256 digest as a byte_array

Most of these byte array functions can be used within at-expressions where they will be translated to their SQL equivalents.

Special operators:

• `+` : concatenation
• `[]` : element access

### rowid¶

Primary key of a database record, 64-bit integer, supports only comparison operations

### json¶

Stored in Postgres as `JSON` type, and can be parsed to `text`;

```val json_text = '{ "name": "Alice" }';
val json_value: json = json(json_text);
print(json_value);
```

`json(text)` - create a `json` value from a string; fails if not a valid JSON string

`.to_text(): text` - convert to string

### unit¶

No value; cannot be used explicitly. Equivalent to unit type in Kotlin.

### null¶

Type of `null` expression; cannot be used explicitly

### Simple type aliases¶

• `pubkey` = `byte_array`
• `name` = `text`
• `timestamp` = `integer`
• `tuid` = `text`

## Complex types¶

### entity¶

```entity user {
key pubkey;
index name;
}
```

### struct¶

A struct is similar to an entity, but its instances exist in memory, not in a database.

```struct user {
name: text;
mutable balance: integer = 0;
}
```

Functions available for all `struct` types:

`T.from_bytes(byte_array): T` - decode from a binary-encoded `gtv` (same as `T.from_gtv(gtv.from_bytes(x))`)

`T.from_gtv(gtv): T` - decode from a `gtv`

`T.from_gtv_pretty(gtv): T` - decode from a pretty-encoded `gtv`

`.to_bytes(): byte_array` - encode in binary format (same as `.to_gtv().to_bytes()`)

`.to_gtv(): gtv` - convert to a `gtv`

`.to_gtv_pretty(): gtv` - convert to a pretty `gtv`

### enum¶

```enum account_type {
single_key_account,
multi_sig_account
}

entity account {
key id: byte_array;
mutable account_type;
mutable args: byte_array;
}
```

Assuming `T` is an enum type:

`T.values(): list<T>` - returns all values of the enum, in the order of declaration

`T.value(text): T` - finds a value by name, throws en exception if not found

`T.value(integer): T` - finds a value by index, throws an exception if not found

Enum value properties:

`.name: text` - the name of the enum value

`.value: integer` - the numeric value (index) associated with the enum value

### T? - nullable type¶

```val nonexistent_user = user @? { .name == "Nonexistent Name" };
require_not_empty(nonexistent_user); // Throws exception because user doesn't exists
```
• Entity attributes cannot be nullable.
• Can be used with almost any type (except nullable, `unit`, `null`).
• Nullable nullable (`T??` is not allowed).
• Normal operations of the underlying type cannot be applied directly.
• Supports `?:`, `?.` and `!!` operators (like in Kotlin).

Compatibility with other types:

• Can assign a value of type `T` to a variable of type `T?`, but not the other way round.
• Can assign `null` to a variable of type `T?`, but not to a variable of type `T`.
• Can assign a value of type `(T)` (tuple) to a variable of type `(T?)`.
• Cannot assign a value of type `list<T>` to a variable of type `list<T?>`.

Allowed operations:

• Null comparison: `x == null`, `x != null`.
• `??` - null check operator: `x??` is equivalent to `x != null`
• `!!` - null assertion operator: `x!!` returns value of `x` if `x` is not `null`, otherwise throws an exception
• `?:` - Elvis operator: `x ?: y` means `x` if `x` is not `null`, otherwise `y`
• `?.` - safe access: `x?.y` results in `x.y` if `x` is not `null` and `null` otherwise; similarly, `x?.y()` either evaluates and returns `x.y()` or returns `null`
• `require(x)`, `require_not_empty(x)`: throws an exception if `x` is `null`, otherwise returns value of `x`

Examples:

```function f(): integer? { ... }

val x: integer? = f();  // type of "x" is "integer?"
val y = x;              // type of "y" is "integer?"

val i = y!!;            // type of "i" is "integer"
val j = require(y);     // type of "j" is "integer"

val a = y ?: 456;       // type of "a" is "integer"
val b = y ?: null;      // type of "b" is "integer?"

val p = y!!;            // type of "p" is "integer"
val q = y?.to_hex();    // type of "q" is "text?"

if (x != null) {
val u = x;          // type of "u" is "integer" - smart cast is applied to "x"
} else {
val v = x;          // type of "v" is "integer?"
}
```

### tuple¶

Examples:

• `val single_number : (integer) = (16,)` - one value
• `val invalid_tuple = (789)` - not a tuple (no comma)
• `val user_tuple: (integer, text) = (26, "Bob")` - two values
• `val named_tuple : (x: integer, y: integer) = (32, 26)` - named fields (can be accessed as `named_tuple.x`, `named_tuple.y`)
• `(integer, (text, boolean))` - nested tuple

Tuple types are compatible only if names and types of fields are the same:

• `(x:integer, y:integer)` and `(a:integer,b:integer)` are not compatible.
• `(x:integer, y:integer)` and `(integer,integer)` are not compatible.

• `t[0]`, `t[1]` - by index
• `t.a`, `t.b` - by name (for named fields)

Unpacking tuples:

```val t = (123, 'Hello');
val (n, s) = t;           // n = 123, s = 'Hello'
```

Works for arbitrarily nested tuples:

```val (n, (p, (x, y), q)) = calculate();
```

Special symbol `_` is used to ignore a tuple element:

```val (_, s) = (123, 'Hello'); // s = 'Hello'
```

Variable types can be specified explicitly:

```val (n: integer, s: text) = (123, 'Hello');
```

Unpacking can be used in a loop:

```val l: list<(integer, text)> = get_tuples();
for ((x, y) in l) {
print(x, y);
}
```

### range¶

Can be used in `for` statement:

```for(count in range(10)){
print(count); // prints out 0 to 9
}
```

`range(start: integer = 0, end: integer, step: integer = 1)` - start-inclusive, end-exclusive (as in Python):

• `range(10)` - a range from 0 (inclusive) to 10 (exclusive)
• `range(5, 10)` - from 5 to 10
• `range(5, 15, 4)` - from 5 to 15 with step 4, i. e. `[5, 9, 13]`
• `range(10, 5, -1)` - produces `[10, 9, 8, 7, 6]`
• `range(10, 5, -3)` - produces `[10, 7]`

Special operators:

• `in` - returns `true` if the value is in the range (taking `step` into account)

### gtv¶

A type used to repsesent encoded arguments and results of remote operation and query calls. It may be a simple value (integer, string, byte array), an array of values or a string-keyed dictionary.

Some Rell types are not Gtv-compatible. Values of such types cannot be converted to/from `gtv`, and the types cannot be used as types of operation/query parameters or result.

Rules of Gtv-compatibility:

• `range` is not Gtv-compatible
• a complex type is not Gtv-compatible if a type of its component is not Gtv-compatible

`gtv.from_json(text): gtv` - decode a `gtv` from a JSON string

`gtv.from_json(json): gtv` - decode a `gtv` from a `json` value

`gtv.from_bytes(byte_array): gtv` - decode a `gtv` from a binary-encoded form

`.to_json(): json` - convert to JSON

`.to_bytes(): byte_array` - convert to bytes

`.hash(): byte_array` - returns a cryptographic hash of the value

gtv-related functions:

Functions available for all Gtv-compatible types:

`T.from_gtv(gtv): T` - decode from a `gtv`

`T.from_gtv_pretty(gtv): T` - decode from a pretty-encoded `gtv`

`.to_gtv(): gtv` - convert to a `gtv`

`null.to_gtv()` - Returns the gtv equivalent of null

`.to_gtv_pretty(): gtv` - convert to a pretty `gtv`

`.hash(): byte_array` - returns a cryptographic hash of the value (same as `.to_gtv().hash()`)

Examples:

```val g = [1, 2, 3].to_gtv();
val l = list<integer>.from_gtv(g);   // Returns [1, 2, 3]
print(g.hash());
```

## Collection types¶

Collection types are:

• `list<T>` - an ordered list
• `set<T>` - an unordered set, contains no duplicates
• `map<K,V>` - a key-value map

Collection types are mutable, elements can be added or removed dynamically.

Only a non-mutable type can be used as a `map` key or a `set` element.

Following types are mutable:

• Collection types (`list`, `set`, `map`) - always.
• Nullable type - only if the underlying type is mutable.
• Struct type - if the struct has a mutable field, or a field of a mutable type.
• Tuple - if a type of an element is mutable.

Creating collections:

```// list
val l1 = [ 1, 2, 3, 4, 5 ];
val l2 = list<integer>();

// set
val s = set<integer>();

// map
val m1 = [ 'Bob' : 123, 'Alice' : 456 ];
val m2 = map<text, integer>();
```

### list<T>¶

Ordered collection type. Accept duplication.

```var messages = message @* { } ( @sort timestamp = .timestamp );
```

Constructors:

`list<T>()` - a new empty list

`list<T>(list<T>)` - a copy of the given list (list of subtype is accepted as well)

`list<T>(set<T>)` - a copy of the given set (set of subtype is accepted)

Methods:

`.add(T): boolean` - adds an element to the end, always returns `true`

`.add(pos: integer, T): boolean` - inserts an element at a position, always returns `true`

`.add_all(list<T>): boolean`

`.add_all(set<T>): boolean`

`.add_all(pos: integer, list<T>): boolean`

`.add_all(pos: integer, set<T>): boolean`

`.clear()`

`.contains(T): boolean`

`.contains_all(list<T>): boolean`

`.contains_all(set<T>): boolean`

`.empty(): boolean`

`.index_of(T): integer` - returns `-1` if element is not found

`.remove(T): boolean` - removes the first occurrence of the value, return `true` if found

`.remove_all(list<T>): boolean`

`.remove_all(set<T>): boolean`

`.remove_at(pos: integer): T` - removes an element at a given position

`.size(): integer`

`._sort()` - sorts this list, returns nothing (name is `_sort`, because `sort` is a keyword in Rell)

`.sorted(): list<T>` - returns a sorted copy of this list

`.to_text(): text` - returns e. g. `'[1, 2, 3, 4, 5]'`

`.sub(start: integer[, end: integer]): list<T>` - returns a sub-list (start-inclusive, end-exclusive)

Special operators:

• `[]` - element access (read/modify)
• `in` - returns `true` if the value is in the list

### set<T>¶

Unordered collection type. Does not accept duplication.

```var my_classmates = set<user>();
```

Constructors:

`set<T>()` - a new empty set

`set<T>(set<T>)` - a copy of the given set (set of subtype is accepted as well)

`set<T>(list<T>)` - a copy of the given list (with duplicates removed)

Methods:

`.add(T): boolean` - if the element is not in the set, adds it and returns `true`

`.add_all(list<T>): boolean` - adds all elements, returns `true` if at least one added

`.add_all(set<T>): boolean` - adds all elements, returns `true` if at least one added

`.clear()`

`.contains(T): boolean`

`.contains_all(list<T>): boolean`

`.contains_all(set<T>): boolean`

`.empty(): boolean`

`.remove(T): boolean` - removes the element, returns `true` if found

`.remove_all(list<T>): boolean` - returns `true` if at least one removed

`.remove_all(set<T>): boolean` - returns `true` if at least one removed

`.size(): integer`

`.sorted(): list<T>` - returns a sorted copy of this set (as a list)

`.to_text(): text` - returns e. g. `'[1, 2, 3, 4, 5]'`

Special operators:

• `in` - returns `true` if the value is in the set

### map<K,V>¶

A key/value pair collection type.

```var dictionary = map<text, text>();
dictionary["Mordor"] = "A place where one does not simply walk into";
```

Constructors:

`map<K,V>()` - a new empty map

`map<K,V>(map<K,V>)` - a copy of the given map (map of subtypes is accepted as well)

Methods:

`.clear()`

`.contains(K): boolean`

`.empty(): boolean`

`.get(K): V` - get value by key (same as `[]`)

`.put(K, V)` - adds/replaces a key-value pair

`.keys(): set<K>` - returns a copy of keys

`.put_all(map<K, V>)` - adds/replaces all key-value pairs from the given map

`.remove(K): V` - removes a key-value pair (fails if the key is not in the map)

`.size(): integer`

`.to_text(): text` - returns e. g. `'{x=123, y=456}'`

`.values(): list<V>` - returns a copy of values

Special operators:

• `[]` - get/set value by key
• `in` - returns `true` if a key is in the map

## Virtual types¶

A reduced data structure with Merkle tree. Type `virtual<T>` can be used only with following types `T`:

• `list<*>`
• `set<*>`
• `map<text, *>`
• `struct`
• `tuple`

Additionally, types of all internal elements of `T` must satisfy following constraints:

• must be Gtv-compatible
• for a `map` type, the key type must be `text` (i. e. `map<text, *>`)

Operations available for all virtual types:

• member access: `[]` for `list` and `map`, `.name` for `struct` and tuple
• `.to_full(): T` - converts the virtual value to the original value, if the value is full (all internal elements are present), otherwise throws an exception
• `.hash(): byte_array` - returns the hash of the value, which is the same as the hash of the original value.
• `virtual<T>.from_gtv(gtv): virtual<T>` - decodes a virtual value from a Gtv.

Features of `virtual<T>`:

• it is immutable
• reading a member of type `list<*>`, `map<*,*>`, `struct` or tuple returns a value of the corresponding virtual type, not of the actual member type
• cannot be converted to Gtv, so cannot be used as a return type of a `query`

Example:

```struct rec { t: text; s: integer; }

operation op(recs: virtual<list<rec>>) {
for (rec in recs) {                 // type of "rec" is "virtual<rec>", not "rec"
val full = rec.to_full();       // type of "full" is "rec", fails if the value is not full
print(full.t);
}
}
```

### virtual<list<T>>¶

`virtual<list<T>>.from_gtv(gtv): virtual<list<T>>` - decodes a Gtv

`.empty(): boolean`

`.get(integer): virtual<T>` - returns an element, same as `[]`

`.hash(): byte_array`

`.size(): integer`

`.to_full(): list<T>` - converts to the original value, fails if the value is not full

`.to_text(): text` - returns a text representation

Special operators:

• `[]` - element read, returns `virtual<T>` (or just `T` for simple types)
• `in` - returns `true` if the given integer index is present in the virtual list

### virtual<set<T>>¶

`virtual<set<T>>.from_gtv(gtv): virtual<set<T>>` - decodes a Gtv

`.empty(): boolean`

`.hash(): byte_array`

`.size(): integer`

`.to_full(): set<T>` - converts to the original value, fails if the value is not full

`.to_text(): text` - returns a text representation

Special operators:

• `in` - returns `true` if the given value is present in the virtual set; the type of the operand is `virtual<T>>` (or just `T` for simple types)

### virtual<map<K,V>>¶

`virtual<map<K,V>>.from_gtv(gtv): virtual<map<K,V>>` - decodes a Gtv

`.contains(K): boolean` - same as operator `in`

`.empty(): boolean`

`.get(K): virtual<V>` - same as operator `[]`

`.hash(): byte_array`

`.keys(): set<K>` - returns a copy of keys

`.size(): integer`

`.to_full(): map<K,V>` - converts to the original value, fails if the value is not full

`.to_text(): text` - returns a text representation

`.values(): list<virtual<V>>` - returns a copy of values (if `V` is a simple type, returns `list<V>`)

Special operators:

• `[]` - get value by key, fails if not found, returns `virtual<V>` (or just `V` for simple types)
• `in` - returns `true` if a key is in the map

### virtual<struct>¶

`virtual<R>.from_gtv(gtv): R` - decodes a Gtv

`.hash(): byte_array`

`.to_full(): R` - converts to the original value, fails if the value is not full

## Subtypes¶

If type `B` is a subtype of type `A`, a value of type `B` can be assigned to a variable of type `A` (or passed as a parameter of type `A`).

• `T` is a subtype of `T?`.
• `null` is a subtype of `T?`.
• `(T,P)` is a subtype of `(T?,P?)`, `(T?,P)` and `(T,P?)`.