SESI PROGRAMMING LANGUAGE DOCS
⌕

Functions in Sesi

Functions are named, reusable blocks of logic declared with the fn keyword. They accept typed parameters, optionally return a value, and can capture the enclosing scope as closures.

---

Declaration

fn_stmt := 'async'? 'fn' identifier '(' parameters ')' '->' type? block
parameters := (identifier ':' type ('=' expr)?)? (',' identifier ':' type ('=' expr)?)*

fn greet(name: string) {
  show "Hello," name
}

greet("Ada")   // Hello, Ada

Note: There are no function, def, or func keywords. Always use fn.

---

Documentation Comments

Write a comment immediately above each public or nontrivial declaration. Sesi shows these comments in hover details and preserves function documentation when functions are exported and imported. Documented handlers can also supply descriptions to modules such as std/api.

Any comment format works, and a plain description is enough. The preferred schema uses a description followed by Parameters and Returns sections:

/**
  Adds two numbers.

  Parameters:
  - `a`: The first number.
  - `b`: The second number.

  Returns:
   The sum of `a` and `b`.

  Example:
  ```sesi
  show add(2, 3)   // 5
  ```
*/
fn add(a: number, b: number) -> number {
  return a + b
}

Keep each comment directly adjacent to its declaration so tools can associate them.

---

Parameters

Typed Parameters

Parameter types are optional but recommended for clarity:

fn add(a: number, b: number) {
  return a + b
}

show add(10, 5)   // 15

Default Parameters

Parameters can have default values. They are used when the caller omits that argument:

fn greet(name: string = "World") {
  show "Hello," name
}

greet()        // Hello, World
greet("Sesi")  // Hello, Sesi

Untyped Parameters

Types can be omitted entirely for quick utility functions:

fn double(x) {
  return x * 2
}

Type Aliases

num and str are valid aliases for number and string in parameter lists:

fn format(label: str, value: num) {
  return label + ": " + str(value)
}

---

Return Values

Use return inside a fn block to send a value back to the caller. return is also valid at the top level, where it immediately stops the current script or module. A top-level return value is accepted, but there is no caller to receive it, so a bare return is usually clearest for early exits.

fn square(x: number) {
  return x * x
}

let result = square(9)
show result   // 81

A function without an explicit return produces null.

Return Type Annotations

Annotate the return type with -> after the parameter list:

fn multiply(a: number, b: number) -> number {
  return a * b
}

fn status(ok: bool) -> string {
  if ok { return "ok" }
  return "failed"
}

---

Calling Functions

Call a function by name with arguments in parentheses:

fn sum(a, b) { return a + b }

let total = sum(3, 7)
show total   // 10

---

Closures

Functions capture variables from the surrounding scope at definition time. The captured binding is live — mutations to the outer variable are visible inside the function:

let base = 100

fn addBase(n) { return n + base }

show addBase(5)   // 105
base = 200
show addBase(5)   // 205

---

Functions as Values

Functions are first-class values. You can pass them as arguments to other functions — this is the foundation of map, filter, reduce, and find:

fn isEven(x) { return x % 2 == 0 }

let numbers = [1, 2, 3, 4, 5, 6]
let evens   = filter(numbers, isEven)   // [2, 4, 6]

fn square(x)     { return x * x }
fn sum(acc, x)   { return acc + x }

let nums    = [1, 2, 3, 4, 5]
let squares = map(nums, square)     // [1, 4, 9, 16, 25]
let total   = reduce(nums, sum)     // 15

---

The Pipe Operator

The | operator passes the result of the left expression as the first argument to the function on the right. Use it to compose a chain of transformations without nesting:

fn add(a, b) { return a + b }
fn mul(a, b) { return a * b }

// 10 → add(5) → 15 → mul(2) → 30
let result = 10 | add(5) | mul(2)
show result   // 30

---

Async Functions

Prefix fn with async to declare a function that returns a Sesi Promise. Use await at the call site to block and resolve the value:

async fn fetchGreeting(name: string) {
  return "Hello, " + name
}

let p        = fetchGreeting("Sesi")   // Promise
let greeting = await p                 // "Hello, Sesi"
show greeting

Note: Model calls inside a script are blocking by default. async/await is used when you explicitly want deferred execution.

---

Exporting Functions

Mark a function with export to make it importable from other .sesi files:

// logger.sesi
export fn info(message: string) {
  show "[INFO]" message
}

export fn warn(message: string) {
  show "[WARN]" message
}

Import it in another script using allow ... in with:

allow "logger" in with {info, warn}

info("Script started")
warn("Using default configuration")

allow "logger" in with log

log.info("Script started")
log.warn("Using default configuration")

---

Quick Reference

// Basic function
fn greet(name: string) { show "Hello," name }
greet("Ada")

// With return value and type annotation
fn add(a: number, b: number) -> number { return a + b }
let sum = add(3, 7)

// Default parameter
fn greet(name: string = "World") { show "Hello," name }
greet()

// Untyped
fn double(x) { return x * 2 }

// First-class — pass as argument
fn isEven(x) { return x % 2 == 0 }
let evens = filter([1, 2, 3, 4], isEven)

// Pipe composition
let result = 10 | add(5) | double

// Async
async fn load(path: string) { return read_file(path) }
let content = await load("data.txt")

// Export
export fn compute(x: number) -> number { return x * x }

---