Built-in Functions

These functions are built into the language and always available — no imports needed.

Program Control

exit()

(int) -> void

Exits the program with the specified status code.

// Exit with success
exit(EXIT_SUCCESS)

// Exit with failure
exit(EXIT_FAILURE)

// Exit with custom code
exit(42)

Parameters: An integer status code (0 for success, non-zero for failure).

Returns: Does not return (terminates the program).

panic()

(string) -> void

Terminates the program immediately with a panic message. The message is prefixed with [PANIC].

panic("something went wrong")
// Output: [PANIC] something went wrong

Parameters: A string message describing why the program is panicking.

Returns: Does not return (terminates the program).

Error CodeCondition
E5021Panic called

assert()

(bool, string) -> void

Checks that a condition is true. If the condition is false, terminates the program with an assertion failure message prefixed with [ASSERT].

temp x int = 5
assert(x > 0, "x must be positive")  // Passes

temp y int = -1
assert(y > 0, "y must be positive")  // Fails: [ASSERT] y must be positive

Parameters:

  • condition: A boolean expression to check
  • message: A string message to display if the assertion fails

Returns: Nothing if the condition is true; terminates the program if false.

Error CodeCondition
E5022Assertion failed

Constants

EXIT_SUCCESS

int constant with value 0

Represents a successful program exit.

exit(EXIT_SUCCESS)  // Exit with code 0

EXIT_FAILURE

int constant with value 1

Represents a failed program exit.

exit(EXIT_FAILURE)  // Exit with code 1

Utility Functions

input()

() -> string

Reads a line of text from stdin.

import @std

std.print("Enter your name: ")
temp name string = input()
std.println("Hello, " + name)

read_int()

() -> (int, Error)

Reads an integer from stdin. Returns a tuple of the parsed integer and an error (if the input cannot be parsed).

import @std

std.print("Enter a number: ")
temp num, err = read_int()

if err != nil {
    std.println("Invalid input: " + err.message)
} otherwise {
    std.println("You entered: " + string(num))
}

len()

(value) -> int

Returns the length of a string, array, or map.

import @std

temp name string = "Hello"
std.println(len(name))  // 5

temp nums [int] = {1, 2, 3}
std.println(len(nums))  // 3

temp ages map[string:int] = {"Alice": 30, "Bob": 25}
std.println(len(ages))  // 2

range()

Generates a sequence of numbers for for loops.

import @std

// range(start, end) - end is exclusive
for i in range(0, 5) {
    std.println(i)  // 0, 1, 2, 3, 4
}

// range(start, end, step)
for i in range(0, 10, 2) {
    std.println(i)  // 0, 2, 4, 6, 8
}

typeof()

(value) -> string

Returns the type of a value as a string.

import @std

temp x int = 42
std.println(typeof(x))  // "int"

temp arr [string] = {"a", "b"}
std.println(typeof(arr))  // "array"

copy()

(value) -> value

Creates a deep copy of a value. Since EZ uses copy-by-default semantics, this function is primarily useful when you want to be explicit about copying.

const Person struct {
    name string
    age int
}

temp a Person = Person{name: "Alice", age: 30}
temp b Person = copy(a)  // Explicit copy (same as just `temp b = a`)
b.age = 31
// a.age is still 30 - b is an independent copy

Note: With copy-by-default behavior, temp b = a already creates an independent copy. Use copy() when you want to be explicit about your intent, or use ref() when you need shared data.

Deep copy behavior:

  • Primitives return themselves
  • Nested structs are recursively copied
  • Arrays are copied with all elements
  • Maps are copied with all key-value pairs

ref()

(value) -> reference

Creates a reference to a value, enabling shared data between variables. Use this when multiple variables need to point to the same underlying data.

const Person struct {
    name string
    age int
}

temp a Person = Person{name: "Alice", age: 30}
temp b Person = ref(a)  // b references the same data as a
b.age = 31
// a.age is now 31 - both variables share the same data

When to use ref():

  • When multiple variables need to share and modify the same data
  • When passing large data structures without copying overhead
  • When you need changes in one place to be visible everywhere

Works with all types:

  • Primitives (int, float, string, bool, char)
  • Complex types (structs, arrays, maps)
// Reference to an array
temp original [int] = {1, 2, 3}
temp shared [int] = ref(original)
shared[0] = 100
// original[0] is now 100

// Reference to a primitive
temp count int = 0
temp counter int = ref(count)
counter++
// count is now 1

Note: Without ref(), assignments create independent copies by default.

new()

(StructType) -> StructType

Creates a new instance of a struct with all fields set to their zero values.

const Person struct {
    name string
    age int
    active bool
}

temp p Person = new(Person)
// p.name = ""
// p.age = 0
// p.active = false

Zero values by type:

  • string""
  • int, float0
  • boolfalse
  • char'\0'
  • Arrays → empty array
  • Maps → empty map

Type Conversion Functions

Convert values between types. These are essential for working with user input, formatting output, and data transformations.

cast()

(value, type) -> value

Converts a value to the specified type. Unlike other conversion functions, cast() accepts any valid EZ type as its second argument, including sized integers and array types.

// Single value conversion
temp x = cast(42, u8)        // int -> u8
temp y = cast(3.14, int)     // float -> int
temp z = cast(65, char)      // int -> char

// Array element-wise conversion
temp bytes [byte] = {65, 66, 67}
temp u8_arr = cast(bytes, [u8])  // [byte] -> [u8]

temp nums [int] = {1, 2, 3}
temp strs = cast(nums, [string])  // ["1", "2", "3"]

Parameters:

  • value — The value or array to convert
  • type — Target type (e.g., u8, int, string, [u8])

Returns: The converted value in the target type.

Target TypeAccepted Source Types
intint, float, string, char, byte
floatfloat, int, string, byte, char
stringall types (uses string representation)
charchar, int, float, byte, string (len=1)
bytebyte, int, float, char, string
i8/i16/i32/i64/i128/i256int, float, string, byte, char
u8/u16/u32/u64/u128/u256int, float, string, byte, char
f32/f64float, int, string, byte, char
boolbool, int (0=false, else=true), string (“true”/“false”)

Error handling: Invalid conversions produce errors with details:

temp result = cast([-1, 2, 3], [u8])
// Error: "cast failed at index 0: value -1 out of u8 range (0 to 255)"

Note: cast() is technically a language keyword because the type argument is validated at check-time (before execution). See also: cast in Keywords.

int()

(value) -> int

Converts a value to an integer.

temp s string = "42"
temp n int = int(s)  // 42

temp f float = 3.9
temp i int = int(f)  // 3 (truncates)

float()

(value) -> float

Converts a value to a float.

temp n int = 42
temp f float = float(n)  // 42.0

temp s string = "3.14"
temp pi float = float(s)  // 3.14

string()

(value) -> string

Converts a value to a string.

temp n int = 42
temp s string = string(n)  // "42"

temp b bool = true
temp bs string = string(b)  // "true"

char()

(int) -> char

Converts an integer (ASCII/Unicode value) to a character.

temp x int = 65
temp c char = char(x)  // 'A'

temp newline char = char(10)  // newline character

byte()

(int) -> byte

Converts an integer to a byte (constrained to 0-255).

temp n int = 65
temp b byte = byte(n)  // 65

temp max byte = byte(255)  // 255
temp wrapped byte = byte(256)  // Error: value out of range

Behavior:

  • Values 0-255 convert directly
  • Values outside 0-255 range produce an error
  • Useful when working with the @bytes module

Sized Integer Conversions

These functions convert values to explicitly sized integer types with range validation. They accept int, float, string, byte, or char values. Out-of-range values produce error E3022.

i8()

(value) -> i8

Converts to a signed 8-bit integer. Range: -128 to 127.

temp small i8 = i8(42)       // 42
temp neg i8 = i8(-100)       // -100
temp from_str i8 = i8("50")  // 50
// i8(200)  // Error E3022: value 200 out of i8 range (-128 to 127)

i16()

(value) -> i16

Converts to a signed 16-bit integer. Range: -32,768 to 32,767.

temp val i16 = i16(1000)      // 1000
temp neg i16 = i16(-30000)    // -30000
// i16(40000)  // Error E3022: out of i16 range

i32()

(value) -> i32

Converts to a signed 32-bit integer. Range: -2,147,483,648 to 2,147,483,647.

temp val i32 = i32(100000)    // 100000
temp neg i32 = i32(-100000)   // -100000

i64()

(value) -> i64

Converts to a signed 64-bit integer. Range: -9.2 quintillion to 9.2 quintillion.

temp val i64 = i64(1000000000)  // 1000000000

i128()

(value) -> i128

Converts to a signed 128-bit integer. Range: -2^127 to 2^127-1.

temp val i128 = i128(1000000000000)  // 1000000000000

i256()

(value) -> i256

Converts to a signed 256-bit integer. Range: -2^255 to 2^255-1.

temp val i256 = i256(1000000000000)  // 1000000000000

u8()

(value) -> u8

Converts to an unsigned 8-bit integer. Range: 0 to 255.

temp val u8 = u8(200)          // 200
temp from_char u8 = u8('A')   // 65
// u8(-1)   // Error E3022: value -1 out of u8 range (0 to 255)
// u8(256)  // Error E3022: value 256 out of u8 range (0 to 255)

u16()

(value) -> u16

Converts to an unsigned 16-bit integer. Range: 0 to 65,535.

temp val u16 = u16(50000)  // 50000
// u16(-1)  // Error E3022: out of u16 range

u32()

(value) -> u32

Converts to an unsigned 32-bit integer. Range: 0 to 4,294,967,295.

temp val u32 = u32(3000000000)  // 3000000000

u64()

(value) -> u64

Converts to an unsigned 64-bit integer. Range: 0 to 18,446,744,073,709,551,615.

temp val u64 = u64(10000000000)  // 10000000000

u128()

(value) -> u128

Converts to an unsigned 128-bit integer. Range: 0 to 2^128-1.

temp val u128 = u128(1000000000000)  // 1000000000000

u256()

(value) -> u256

Converts to an unsigned 256-bit integer. Range: 0 to 2^256-1.

temp val u256 = u256(1000000000000)  // 1000000000000

Accepted Source Types (all sized integers)

Source TypeExample
inti8(42)
floatu16(3.14) → truncates to 3
stringi32("100")
byteu8(byte(65))
chari16('A')65

Sized Float Conversions

f32()

(value) -> f32

Converts to a 32-bit floating-point number. Truncates precision to float32 range.

temp val f32 = f32(3.14159265358979)  // 3.1415927 (reduced precision)
temp from_int f32 = f32(42)           // 42.0
temp from_str f32 = f32("2.5")       // 2.5

f64()

(value) -> f64

Converts to a 64-bit floating-point number. Full double-precision range.

temp val f64 = f64(3.14159265358979)  // 3.14159265358979 (full precision)
temp from_int f64 = f64(42)           // 42.0
temp from_str f64 = f64("2.5")       // 2.5

Accepted Source Types (f32/f64)

Source TypeExample
floatf32(3.14)
intf64(42)
stringf32("2.5")
bytef64(byte(65))
charf32('A')65.0

Error Handling

Error Type

The Error type represents an error value that can be returned from functions.

FieldTypeDescription
messagestringThe error message
codestringError code (empty for user errors, E-codes for stdlib errors)

Checking for errors:

import @std
using std

temp err = validate("")
if err != nil {
    println("Error: ${err.message}")
}

error()

(string) -> Error

Creates a user-defined error. Returns an Error with .message set to the argument and .code set to empty string.

Single error return:

import @std
using std

do validate(name string) -> Error {
    if len(name) == 0 {
        return error("name cannot be empty")
    }
    return nil
}

do main() {
    temp err = validate("")
    if err != nil {
        println(err.message)  // "name cannot be empty"
    }
}

Tuple return (value + error):

import @std
using std

do divide(a int, b int) -> (int, Error) {
    if b == 0 {
        return 0, error("division by zero")
    }
    return a / b, nil
}

do main() {
    temp result, err = divide(10, 0)
    if err != nil {
        println("Failed: ${err.message}")
    } otherwise {
        println("Result: ${result}")
    }
}
Error CodeCondition
E7001Wrong number of arguments
E7003Argument is not a string

Quick Reference

Functions

FunctionDescriptionExample
exit(code)Exit program with status codeexit(EXIT_SUCCESS)
panic(msg)Terminate with panic messagepanic("error")
assert(cond, msg)Assert condition is trueassert(x > 0, "must be positive")
input()Read line from stdintemp name = input()
read_int()Read integer from stdintemp num, err = read_int()
len(x)Length of string, array, or maplen("hello")5
range(start, end)Number sequence for loopsrange(0, 5)0,1,2,3,4
range(start, end, step)Number sequence with steprange(0, 10, 2)0,2,4,6,8
typeof(x)Type name as stringtypeof(42)"int"
copy(x)Explicit deep copy of a valuecopy(myStruct) → independent copy
ref(x)Create reference for shared dataref(myStruct) → shared reference
new(Type)Create zero-initialized structnew(Person) → struct with zero values
cast(x, type)Convert to any typecast(42, u8)42 as u8
int(x)Convert to integerint("42")42
float(x)Convert to floatfloat(42)42.0
string(x)Convert to stringstring(42)"42"
char(x)Convert int to characterchar(65)'A'
byte(x)Convert int to byte (0-255)byte(65)65
i8(x)Convert to signed 8-bit inti8(42)42 as i8
i16(x)Convert to signed 16-bit inti16(1000)1000 as i16
i32(x)Convert to signed 32-bit inti32(100000)100000 as i32
i64(x)Convert to signed 64-bit inti64(val) → value as i64
i128(x)Convert to signed 128-bit inti128(val) → value as i128
i256(x)Convert to signed 256-bit inti256(val) → value as i256
u8(x)Convert to unsigned 8-bit intu8(200)200 as u8
u16(x)Convert to unsigned 16-bit intu16(50000)50000 as u16
u32(x)Convert to unsigned 32-bit intu32(val) → value as u32
u64(x)Convert to unsigned 64-bit intu64(val) → value as u64
u128(x)Convert to unsigned 128-bit intu128(val) → value as u128
u256(x)Convert to unsigned 256-bit intu256(val) → value as u256
f32(x)Convert to 32-bit floatf32(3.14) → reduced precision
f64(x)Convert to 64-bit floatf64(42)42.0 as f64
error(msg)Create user-defined errorerror("invalid input")Error

Constants

ConstantValueDescription
EXIT_SUCCESS0Successful program exit
EXIT_FAILURE1Failed program exit