Keywords Reference
EZ uses plain English keywords designed to be readable and beginner-friendly. If you’re coming from another language, some of these will look different from what you’re used to.
Variables & Constants
mut
Declares a mutable variable — a value that can change.
mut count = 0
count = 10 // allowed
mut name string = "Alice" // explicit type annotation also works
Why “mut”? It stands for “mutable.” Use mut for values that will change during your program’s execution, like counters, user input, or calculated results.
const
Declares an immutable constant — a value that cannot change. Also used to define structs and enums.
const PI = 3.14159
const MAX_SIZE = 100
// Also used for type definitions
const Person struct {
name string
age int
}
Why “const”? It’s short for “constant.” Use const for values that should never change, like configuration settings, mathematical constants (PI), or type definitions.
Functions
do
Declares a function.
do greet(name string) {
println("Hello, ${name}!")
}
do add(a, b int) -> int {
return a + b
}
Why “do”? Functions do things. Reads naturally: “do greet” means “do the greet action.”
return
Exits a function and optionally returns a value.
do double(x int) -> int {
return x * 2
}
do validate(n int) -> bool {
if n < 0 {
return false // early return
}
return true
}
ensure
Guarantees a function call runs when the current function exits, regardless of how it exits. Used for cleanup like closing files or databases.
import @io
do process() {
mut file, _ = io.open("data.txt", "r")
ensure io.close(file) // always runs when process() exits
// ... work with file ...
// io.close(file) runs automatically, even on early return
}
Multiple ensure statements run in reverse order (last registered runs first). See Functions - ensure for details.
or_return
Provides error propagation shorthand. When a call returns a non-nil error, or_return immediately returns from the enclosing function.
do load() -> (string, Error) {
// Bare or_return: propagates the error with zero values
mut content = read_file("data.txt") or_return
mut parsed = json.decode(content) or_return
return parsed, nil
}
// With custom fallback values:
mut content = read_file("data.txt") or_return "", error("failed to load")
The enclosing function must have Error as its last return type. See Functions - or_return for details.
Control Flow
if
Starts a conditional block. Executes code only if the condition is true.
if score >= 90 {
println("A grade!")
}
or
Adds an alternative condition — like else if in other languages.
if score >= 90 {
println("A")
} or score >= 80 {
println("B")
} or score >= 70 {
println("C")
}
Why “or”? Reads naturally: “if this, or that, or that.”
otherwise
The fallback case — like else in other languages.
if score >= 60 {
println("Pass")
} otherwise {
println("Fail")
}
Why “otherwise”? Reads like English: “if passing, celebrate; otherwise, study more.”
for
Numeric loop that iterates over a range.
for i in range(0, 5) {
println(i) // 0, 1, 2, 3, 4
}
for_each
Iterates over each item in a collection (arrays, strings, or maps).
mut names [string] = {"Alice", "Bob", "Charlie"}
for_each name in names {
println("Hello, ${name}")
}
// With maps
mut ages map[string:int] = {"Alice": 30, "Bob": 25}
for_each k, v in ages {
println("${k}: ${v}")
}
Why “for_each”? Explicit about looping over each item in a collection.
as_long_as
Loops while a condition is true — like while in other languages.
mut count = 0
as_long_as count < 5 {
println(count)
count++
}
Why “as_long_as”? Reads naturally: “keep going as long as this is true.”
while
Alias for as_long_as. Both are valid:
mut total = 0
while total < 100 {
total += 10
}
loop
Infinite loop that runs until you break out of it.
mut attempts = 0
loop {
attempts++
if attempts >= 3 {
break
}
}
break
Immediately exits the current loop.
for i in range(0, 100) {
if i == 5 {
break // stop at 5
}
println(i)
}
continue
Skips to the next iteration of the loop.
for i in range(0, 10) {
if i % 2 == 0 {
continue // skip even numbers
}
println(i) // only odd numbers
}
in
Used with for to iterate over a range, or to check if a value exists in a collection (arrays, ranges, or maps).
// In a for loop
for i in range(0, 10) {
println(i)
}
// Array membership
mut nums [int] = {1, 2, 3}
if 2 in nums {
println("Found it!")
}
// Map key membership
mut ages map[string:int] = {"Alice": 30}
if "Alice" in ages {
println("Key exists!")
}
not_in
Checks if a value does not exist in a collection (arrays, ranges, or maps). !in is a shorthand alias.
mut nums [int] = {1, 2, 3}
if 5 not_in nums {
println("Not found")
}
// !in is shorthand for not_in
mut ages map[string:int] = {"Alice": 30}
if "Bob" !in ages {
println("Bob not in map")
}
when
Starts a pattern matching block — like switch in other languages.
mut day = 3
when day {
is 1 { println("Monday") }
is 2 { println("Tuesday") }
is 3 { println("Wednesday") }
default { println("Other day") }
}
Why “when”? Reads naturally: “when day is 1, do this.”
is
Specifies a case in a when block.
when value {
is 1 { /* matches 1 */ }
is 2, 3, 4 { /* matches 2, 3, or 4 */ }
is range(5, 10) { /* matches 5-9 */ }
default { /* fallback */ }
}
default
The fallback case in a when block — executes when no is case matches.
when status {
is "active" { /* handle active */ }
is "pending" { /* handle pending */ }
default { /* handle all other cases */ }
}
Note: default is required unless using #strict with an enum that has all cases covered.
Type Conversion
cast
Converts values between types. Works with single values and arrays.
// Single value conversion
mut x = cast(42, u8) // int -> u8
mut y = cast(3.14, int) // float -> int
mut z = cast(65, char) // int -> char
// Array element-wise conversion
mut bytes [byte] = {65, 66, 67}
mut u8_arr = cast(bytes, [u8]) // [byte] -> [u8]
Why “cast”? Explicitly converts (casts) a value from one type to another.
Note: Although cast() uses function-like syntax, it is a language keyword, not a regular function. The second argument must be a valid EZ type, which the compiler validates at check-time (before execution). This is why cast() is documented here rather than solely in built-in functions — though it also appears there for discoverability. See also: cast() in Built-in Functions.
Supported Conversions
| Target Type | Accepted Source Types |
|---|---|
int | int, float, string, char, byte |
float | float, int, string, byte, char |
string | all types (uses string representation) |
char | char, int, float, byte, string (len=1) |
byte | byte, int, float, char, string |
i8/i16/i32/i64/i128/i256 | int, float, string, byte, char |
u8/u16/u32/u64/u128/u256 | int, float, string, byte, char |
f32/f64 | float, int, string, byte, char |
bool | bool, int (0=false, else=true), string (“true”/“false”) |
Array Conversions
For arrays, cast() applies the conversion to each element:
mut nums [int] = {1, 2, 3}
mut strs = cast(nums, [string]) // ["1", "2", "3"]
Error Handling
Invalid conversions produce errors:
// Range error with index info
mut result = cast([-1, 2, 3], [u8])
// Error: "cast failed at index 0: value -1 out of u8 range (0 to 255)"
Types
Primitive Types
| Keyword | Description |
|---|---|
int | Integer (whole number) |
uint | Unsigned (non-negative) integer |
float | Floating-point (decimal) number |
string | Text |
char | Single character |
bool | Boolean (true or false) |
byte | Unsigned 8-bit value (0-255) |
mut age = 25
mut price = 19.99
mut name = "Alice"
mut letter = 'A'
mut active = true
Sized Integers
For when you need precise control over size:
| Signed | Unsigned | Size |
|---|---|---|
i8 | u8 | 8 bits |
i16 | u16 | 16 bits |
i32 | u32 | 32 bits |
i64 | u64 | 64 bits |
i128 | u128 | 128 bits |
i256 | u256 | 256 bits |
mut byte_val u8 = 255
mut big i64 = 9223372036854775807
struct
Defines a composite type with named fields.
const Point struct {
x int
y int
}
mut p = Point{x: 10, y: 20}
enum
Defines a type with a fixed set of named values.
const Status enum {
PENDING
ACTIVE
DONE
}
mut s = Status.ACTIVE
map
Key-value collection type.
mut ages map[string:int] = {
"Alice": 30,
"Bob": 25
}
Modules
import
Brings a module into your file. Must be at the top.
import @math, @arrays
import "./mymodule"
using
Makes module contents available without a prefix.
import @math
using math
do main() {
mut result = sqrt(16.0) // No math. prefix needed
}
import and use
Combines importing and using in one statement:
import and use @arrays
do main() {
mut nums [int] = {1, 2, 3}
append(nums, 4) // No arrays. prefix needed
}
Pointer Operations
new
Allocates a zero-initialized struct on the heap. Returns a pointer (^Type).
const Person struct {
name string
age int
}
mut p = new(Person) // Returns ^Person
p^.name = "Alice"
p^.age = 30
addr
Gets the memory address of a variable.
mut x = 42
mut p = addr(x) // ^int pointer
println(p^) // 42
Visibility
By default, all user-declared functions, types, and variables are public — they can be accessed from other modules that import yours.
private
Makes a declaration private to its module. Private items cannot be accessed from outside the module.
// Private variable — only accessible within this module
private const INTERNAL_LIMIT = 1000
// Private function — only callable within this module
private do helper() {
// ...
}
// Public function (default) — accessible from other modules
do get_count() -> int {
helper() // can call private function internally
return 42
}
Why “private”? Hides implementation details so other modules only see what they need.
Boolean Values
true
Boolean true value.
false
Boolean false value.
mut isValid = true
mut hasError = false
nil
Represents the absence of a value. Used primarily with Error types to indicate success or check for errors.
import @io
do main() {
mut content, err = io.read("data.txt")
if err != nil {
println("Error:", err.message)
return
}
// err is nil, meaning no error occurred
println(content)
}
Functions that can fail return nil for the error on success, or an Error on failure.
Quick Reference Table
| EZ Keyword | Other Languages | Purpose |
|---|---|---|
mut | let, var | Mutable variable |
const | const, final | Immutable value |
do | function, func, fn, def | Declare function |
or | else if, elif | Alternative condition |
otherwise | else | Fallback condition |
for_each | for...of, for...in, foreach | Iterate collection |
as_long_as | while | Conditional loop |
while | while | Conditional loop (alias) |
loop | while(true), loop | Infinite loop |
when | switch, match | Pattern matching |
is | case | Pattern case |
default | default, _ | Fallback case |
cast | type casts, as | Type conversion |
ensure | defer | Guaranteed cleanup on function exit |
or_return | ? (Rust), try | Error propagation shorthand |
new | new, malloc | Heap-allocate struct (returns pointer) |
nil | null, None, nil | Absence of a value |
private | private, internal | Module-private declaration |
!in | not in | Shorthand for not_in |
For attributes (#doc, #flags, #strict, #suppress), see Attributes.
See Also
- Control Flow — detailed control flow usage and examples
- Functions — detailed function usage and examples
- Variables — detailed variable usage and examples
- Modules — detailed module system usage