Variables & Types
This section covers variable declaration, type inspection, and core built-in types: primitives, arrays, maps, and classes.
Declaring Variables
memory — Temporary Variables
The memory keyword declares temporary variables that are wiped when the function finishes.
endpoint DeclareVar() -> (output U64):
memory n U64 // Initialized to zero
n = 10
memory m = 11 // Type inferred
memory x U64 = 12 // Explicit type
yield output n + m + x
If no value is assigned, variables are initialized to their zero value (0 for integers, false for booleans).
storage — Efficient Pointers
The storage keyword creates a pointer to stored data, enabling efficient operations without transferring large structures to memory.
endpoint dynamic RegisterGuardian(new_guardian Identifier):
storage operator Operator // Declare pointer
observe operators <- GuardianRegistry.Logic.Operators:
operator = operators[Sender]
mutate operators <- GuardianRegistry.Logic.Operators:
append(operator.Guardians, new_guardian)
operators[Sender] = operator
Block Syntax (Syntax Grouping)
Both memory and storage declarations support block syntax to group multiple declarations:
memory:
count = 0
name = "Alice"
limit = 10
storage:
operator Operator
balance U64
const — Constants
Constants are defined at the module level with an explicit type and value:
const MAX_SUPPLY U64 = 1000000
const TOKEN_NAME String = "MyCoin"
typeof() — Type Inspection
Get a string description of any variable's type:
memory x = 42
memory t = typeof(x) // Returns "U64"
Primitive Types
| Type | Description |
|---|---|
Bool | Boolean (true/false) |
Bytes | Byte sequence |
String | Text string |
Identifier | 32-byte identifier |
U64 | Unsigned 64-bit integer |
I64 | Signed 64-bit integer |
U256 | Unsigned 256-bit integer |
Type Conversion
Use typecast operators like U256(5) to convert between types:
| From ↓ / To → | Bool | Bytes | String | Identifier | U64 | I64 | U256 |
|---|---|---|---|---|---|---|---|
| Bool | — | ✗ | __str__ | ✗ | .ToU64() | .ToI64() | .ToU256() |
| Bytes | __bool__ | — | __str__ | __id__ | .ToU64() | .ToI64() | .ToU256() |
| String | __bool__ | .ToBytes() | — | __id__ | .ToU64() | .ToI64() | .ToU256() |
| Identifier | __bool__ | .ToBytes() | __str__ | — | ✗ | ✗ | .ToU256() |
| U64 | __bool__ | .ToBytes() | __str__ | ✗ | — | .ToI64() | .ToU256() |
| I64 | __bool__ | .ToBytes() | __str__ | ✗ | .ToU64() | — | .ToU256() |
| U256 | __bool__ | .ToBytes() | __str__ | __id__ | .ToU64() | .ToI64() | — |
Arrays
Fixed-Length Arrays
Size determined at compile time. Cannot be changed later.
// Literal initialization
memory arr = [3]U64{1, 2, 3}
// Zero-value initialization
memory arr2 = make([3]U64) // [0, 0, 0]
arr2[2] = 4 // [0, 0, 4]
Fixed Array Methods
| Method / Function | Returns | Description |
|---|---|---|
len(arr) | U64 | Returns the length of the array |
arr[index] | Type | Access element at index (0-based) |
Variable-Length Arrays (Varrays)
Dynamic arrays that can grow or shrink.
memory vrr []U64 // Empty varray
append(vrr, 1) // [1]
append(vrr, 2) // [1, 2]
memory last = popend(vrr) // Removes and returns 2
memory vrr2 = make([]U64, 2) // [0, 0]
memory joined = merge(vrr, vrr2)
Varray Methods
| Method / Function | Returns | Description |
|---|---|---|
len(vrr) | U64 | Returns current length |
vrr[index] | Type | Access element at index (0-based) |
append(vrr, item) | — | Add element to the end |
popend(vrr) | Type | Remove and return the last element |
merge(vrr1, vrr2) | []Type | Combine two varrays into a new varray |
make([]Type, size) | []Type | Create varray with size zero-value elements |
memory items []String
append(items, "first")
append(items, "second")
memory count = len(items) // 2
memory last = popend(items) // "second"
memory first = items[0] // "first"
Array vs Varray Comparison
| Feature | Array [N]Type | Varray []Type |
|---|---|---|
| Size | Fixed at compile time | Dynamic |
| Syntax | [3]U64 | []U64 |
append | ✗ Not supported | ✓ Supported |
popend | ✗ Not supported | ✓ Supported |
merge | ✗ Not supported | ✓ Supported |
| Use case | Known data (coordinates) | Lists, queues, stacks |
Maps
Key-value pairs where keys must be unique primitives.
// Initialize empty map
memory mp = make(Map[String]U64)
mp["key"] = 42
// Initialize with literals
memory mp2 = Map[String]U64{"No": 0, "Yes": 1}
// Operations
memory count = len(mp2) // 2
memory merged = merge(mp, mp2) // Combine maps
remove(merged, "No") // Delete key
Map Methods
| Method / Function | Returns | Description |
|---|---|---|
len(map) | U64 | Returns the number of key-value pairs |
map[key] | Value | Access value by key |
map[key] = value | — | Set or update a key-value pair |
map[key]? | Bool | Check if key exists (membership test) |
remove(map, key) | — | Delete a key-value pair |
merge(map1, map2) | Map[K]V | Combine two maps (second overwrites duplicates) |
make(Map[K]V) | Map[K]V | Create an empty map |
Membership Check with ?
Use the ? operator to check if a key exists without accessing the value:
memory m = Map[String]U64{"hi": 42}
memory exists = m["hi"]? // true
memory missing = m["lo"]? // false
// Common pattern: check before access
if m["key"]?:
memory val = m["key"]
generate Keyword
Auto-initialize missing keys with their zero value, avoiding manual existence checks:
// Without generate (requires check)
if !counter[userId]?:
counter[userId] = 0
counter[userId]++
// With generate (single line)
generate counter[userId]++
Use generate when you want to safely increment or modify a map value that may not exist yet. It automatically initializes missing keys to their zero value (0 for integers, "" for strings, etc.).
Classes
Group fields and methods into reusable structures.
class Person:
field name String
field age U64
method GetInfo() -> (info String):
info = f"{self.name} is {self.age}"
method mutate Birthday():
self.age += 1
Usage
memory person = Person{name: "Sam", age: 20}
person.Birthday()
memory info = person.GetInfo()
memory fields = len(person) // Number of fields
Built-in Class Operations
| Method / Function | Returns | Description |
|---|---|---|
len(instance) | U64 | Returns the number of fields in the class |
instance.field | Type | Access a field by name |
instance.method() | varies | Call a method on the instance |
- Use
mutatekeyword for methods that modifyself - Maximum 240 custom methods per class
Special Methods (Dunder Methods)
Override these methods to customize how your class behaves with operators and type conversions:
| Method | Purpose | Triggered By | Signature |
|---|---|---|---|
__eq__ | Equality comparison | a == b | (other T) -> (is_equal Bool) |
__lt__ | Less than comparison | a < b | (other T) -> (is_less Bool) |
__gt__ | Greater than comparison | a > b | (other T) -> (is_greater Bool) |
__bool__ | Boolean conversion | Bool(instance) | () -> (result Bool) |
__str__ | String conversion | String(instance) | () -> (result String) |
__len__ | Custom length | len(instance) | () -> (result U64) |
__id__ | Identifier conversion | Identifier(instance) | () -> (result Identifier) |
__join__ | Merge two instances | join(a, b) | (other T) -> (joined T) |
__event__ | Convert to event | emit | () -> (ev EventType) |
__except__ | Custom error | throw | () -> (err String) |
class Person:
field name String
field age U64
method __eq__(other Person) -> (is_equal Bool):
is_equal = self.name == other.name && self.age == other.age
memory p1 = Person{name: "Sam", age: 20}
memory p2 = Person{name: "Sam", age: 20}
memory same = p1 == p2 // true (uses __eq__)
Example: String Conversion
class Token:
field symbol String
field amount U64
method __str__() -> (result String):
result = f"{self.amount} {self.symbol}"
memory t = Token{symbol: "MOI", amount: 100}
memory s = String(t) // "100 MOI"
Example: Boolean Conversion
class Balance:
field value U64
method __bool__() -> (result Bool):
result = self.value > 0
memory b = Balance{value: 0}
if !Bool(b):
// balance is empty