Variables in Sesi
Variables are the fundamental way to store and name values in Sesi. They are declared with the let keyword and follow lexical (block) scoping rules.
---
Declaration
let_stmt := 'let' identifier (':' type)? ('=' expression)? (';' | newline)
All variables are declared with let. There is no const, var, or any other binding keyword.
let name = "Sesi"
let retries: number = 3
let title: string = "Welcome"
let version = 1.9.0
let active = true
let status = any // can hold any type of value
let missing // declared but uninitialized — value is null
Note:
constis forbidden in Sesi. All bindings are mutable by design.
---
Assignment & Mutation
After declaration, a variable can be reassigned using = without repeating let.
let count = 0
let status = any
status = "active" // reassigning a variable of type `any`
status = 1 // reassigning a variable of type `any` to a number
status = {active: true} // reassigning a variable of type `any` to an object
count = count + 1
count = count + 1
show count // 2
show status.active // true
Object fields and array elements can also be mutated directly:
let scores = [10, 20, 30]
scores[0] = 99
show scores // [99, 20, 30]
let user = {name: "Ada", role: "admin"}
user.role = "developer"
show user.name // Ada
---
Types
Sesi infers the type of a variable from its assigned value. The primitive types are:
| Type | Alias | Example | |
|---|---|---|---|
number |
num |
let x = 42 / let pi = 3.14 |
|
string |
str |
let msg = "hello" |
|
bool |
— | let ready = true |
|
null |
— | let empty = null |
|
any |
— | let value = any |
Collection types are also supported:
let tags = ["sesi", "lang", "v1.9.0"] // array
let config = {theme: "dark", limit: 20} // object
Type Aliases
num is interchangeable with number, and str is interchangeable with string. These aliases are most useful inside function signatures:
fn double(x: num) -> num { return x * 2 }
fn greet(name: str) { show "Hello," name }
Variable Type Annotations
You can explicitly annotate a variable by placing : type after its name and before =. Annotations are optional; use them when you want the declaration to state the expected value type.
let age: number = 42
let username: string = "Ada"
let enabled: bool = true
let tags: array<string> = ["sesi", "lang"]
The any Type
any accepts values of every type and bypasses type checking. It can be used as a type annotation or as a variable initializer.
fn display(value: any) { show value }
let result: any = "pending"
result = 200
result = {complete: true}
Using any directly as an initializer creates an unrestricted variable with an initial runtime value of null:
let status = any
show status // null
status = "active"
status = 1
status = {active: true}
Optional Types (T?)
Append ? to mark a type annotation (parameter, return type, or variable) as optional (value may be null):
fn find_user(id: number) -> object? {
if id == 1 { return {name: "Ada"} }
return null
}
fn greet(name: str, title: str? = null) {
if title { show title name }
else { show name }
}
Union Types (T | U)
A variable or parameter can hold one of several types:
let value: number | string = 42
fn show(value: number | string) { show value }
---
Type Conversion
Explicit conversion is done with the built-in cast functions:
let raw = "42"
let n = num(raw) // 42 (number)
let s = str(n) // "42" (string)
let b = bool(0) // false
These are the only valid conversion primitives. Do not use parseInt, Number(), or any JavaScript-style coercion.
---
Scope & Binding Rules
Sesi uses lexical (block) scoping. A variable is visible from its declaration point to the end of the block it lives in.
| Scope Level | Description | |
|---|---|---|
| Global scope | Top-level module declarations | |
| Function scope | Variables declared inside a fn block |
|
| Block scope | Variables inside if, while, for blocks |
Inner scopes shadow outer scopes — declaring a let with the same name inside a block creates a new binding without affecting the outer one.
let x = 10
fn example() {
let x = 99 // shadows the outer x
show x // 99
}
example()
show x // 10
Closures are supported. Functions capture the enclosing scope at definition time:
let base = 100
fn addBase(n) { return n + base }
show addBase(5) // 105
base = 200
show addBase(5) // 205 — closes over the live binding
---
Built-in Global Variables
Sesi exposes built-in globals and shorthands available in every script:
| Variable / Syntax | Type | Description | ||
|---|---|---|---|---|
args |
array<string> |
Command-line arguments passed to the script (excludes runtime flags and the script path itself) | ||
$VAR |
string \ |
null |
Environment variable lookup shorthand (e.g., $USER, $PORT, $HOME). Equivalent to env("VAR"). |
// Access environment variables directly
show $USER
let port = $PORT || 8080
// Run as: sesi myscript.sesi Alice 30
let name = args[0] // "Alice"
let age = args[1] // "30"
show "Hello," name
---
Uninitialized Variables
Declaring a let without a value sets it to null. Operations on null propagate null rather than throwing (v1.x behavior):
let pending
show pending // null
show pending + 1 // null
---
Quick Reference
// Primitive bindings
let count = 3
let title = "Daily Report"
let ready = true
let missing = null
let flexible = any
// Collections
let scores = [10, 20, 30]
let profile = {name: "Ada", role: "developer"}
// Reassignment
count = count + 1
// Type conversion
let raw = "42"
let answer = num(raw)
// CLI args
let input_path = args[0]
let output_path = args[1]
---