SESI PROGRAMMING LANGUAGE DOCS
⌕

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: const is 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]

---