For Blocks
for blocks let you group functions under a type. Think of them as methods without classes. self is an explicit parameter, not magic.
Basic Usage
Section titled “Basic Usage”type User { name: string, age: number }
for User { fn display(self) -> string { `${self.name} (${self.age})` }
fn isAdult(self) -> boolean { self.age >= 18 }
fn greet(self, greeting: string) -> string { `${greeting}, ${self.name}!` }}The self parameter’s type is inferred from the for block. No annotation needed.
Inline Syntax
Section titled “Inline Syntax”When you only need a single function, you can skip the block:
export for User fn display(self) -> string { `${self.name} (${self.age})`}
export for User fn greet(self, greeting: string) -> string { `${greeting}, ${self.name}!`}This is exactly equivalent to a for block with one function. Export is per-function and goes before for:
// Inline: export goes before `for`export for User fn display(self) -> string { ... }
// Block: export goes before `fn` inside the blockfor User { export fn display(self) -> string { ... }}Both forms can be mixed freely in the same file.
For-block functions are pipe-friendly. self is always the first argument:
user |> display // display(user)user |> greet("Hello") // greet(user, "Hello")This gives you method-call ergonomics without OOP:
const message = user |> greet("Hi") |> String.toUpperGeneric Types
Section titled “Generic Types”For blocks work with generic types:
for Array<User> { fn adults(self) -> Array<User> { self |> Array.filter(.age >= 18) }}
users |> adults // only adult usersImporting For-Block Functions
Section titled “Importing For-Block Functions”When for-block functions are defined in a different file from the type, use import { for Type }:
// Import specific for-block functions by typeimport { for User } from "./user-helpers"import { for Array, for Map } from "./collections"
// Mix with regular importsimport { Todo, Filter, for Array, for string } from "./todo"import { for Type } brings all exported for-block functions for that type from the imported file. For generic types, use the base type only (no type params) — import { for Array } brings all for Array<T> extensions.
Importing a type still auto-imports its for-block functions from the same file. The import { for Type } syntax is for cross-file for-blocks.
Real-World Example
Section titled “Real-World Example”From the todo app, validating input strings and filtering todos:
// Inline for-declarationexport for string fn validate(self) -> Validation { const trimmed = self |> trim const len = trimmed |> String.length match len { 0 -> Empty, 1 -> TooShort, _ -> match len > 100 { true -> TooLong, false -> Valid(trimmed), }, }}
// Block form for grouping related functionsfor Array<Todo> { export fn filterBy(self, f: Filter) -> Array<Todo> { match f { All -> self, Active -> self |> filter(.done == false), Completed -> self |> filter(.done == true), } }
export fn remaining(self) -> number { self |> filter(.done == false) |> length }}Then import them in another file:
import { Todo, Filter } from "./types"import { for string, for Array } from "./todo"
const visible = todos |> filterBy(filter)const remaining = todos |> remainingExport
Section titled “Export”For-block functions can be exported like regular functions:
// Block form: export inside the blockfor User { export fn display(self) -> string { `${self.name} (${self.age})` }}
// Inline form: export before `for`export for User fn display(self) -> string { `${self.name} (${self.age})`}selfis always the explicit first parameter. Its type is inferred.- No
this, no implicit context - Multiple
forblocks per type are allowed, even across files - Compiles to standalone TypeScript functions (no classes)
- Both block and inline syntax are supported and equivalent
What It Compiles To
Section titled “What It Compiles To”for User { fn display(self) -> string { `${self.name} (${self.age})` }}Becomes:
function display(self: User): string { return `${self.name} (${self.age})`;}No class wrappers, no prototype chains. Plain functions.