# Slang - Functional Programming Library for TypeScript Slang is a functional programming library for TypeScript that brings robust error handling and type-safe utilities inspired by languages like Rust. The library provides a comprehensive set of tools for defensive programming, including Result types for explicit error handling, Option types for nullable values, pattern matching, and functional composition utilities. It emphasizes type safety, immutability, and making invalid states unrepresentable through TypeScript's type system. The core philosophy of Slang centers on making error handling explicit and composable. Rather than relying on try-catch blocks or nullable types that can be forgotten, Slang forces developers to handle all possible states through discriminated unions and exhaustive pattern matching. The library includes utilities for wrapping unsafe operations, transforming data through pipelines, combining collections, and creating unique identifiers, all while maintaining full type inference and compile-time safety guarantees. ## API Reference ### Result Type - Explicit Error Handling Result represents operations that can succeed with `Ok` or fail with `Err`. Forces explicit error handling instead of exceptions. ```typescript import { Ok, Err, type Result } from "slang-ts"; // Basic division with error handling function divide(a: number, b: number): Result { if (b === 0) return Err("Cannot divide by zero"); return Ok(a / b); } const result1 = divide(10, 2); if (result1.isOk) { console.log("Success:", result1.value); // 5 } else { console.log("Error:", result1.error); } const result2 = divide(10, 0); if (result2.isErr) { console.log("Error:", result2.error); // "Cannot divide by zero" } // Async API example interface User { id: string; name: string; email: string; } async function fetchUser(id: string): Promise> { try { const response = await fetch(`https://api.example.com/users/${id}`); if (!response.ok) { return Err("User not found"); } const user = await response.json(); return Ok(user); } catch (error) { return Err("Network error"); } } const user = await fetchUser("123"); if (user.isOk) { console.log("User:", user.value.name, user.value.email); } else { console.log("Failed to fetch user:", user.error); } ``` ### Option Type - Safe Nullable Value Handling Option wraps values that may or may not be present. Returns `Some` for truthy values, `None` for null/undefined/empty strings. Note: 0 and false are considered truthy. ```typescript import { option } from "slang-ts"; // Basic usage const a = option("hello"); // Some("hello") const b = option(null); // None const c = option(0); // Some(0) - zero is truthy! const d = option(""); // None const e = option(false); // Some(false) - false is truthy! const f = option(undefined); // None if (a.isSome) { console.log("Value:", a.value); // "hello" } if (b.isNone) { console.log("No value present"); } // Environment variable handling const port = option(process.env.PORT); if (port.isSome) { console.log("Server will run on port:", port.value); } else { console.log("No PORT configured, using default"); } // Configuration parsing function getConfig() { const apiKey = option(process.env.API_KEY); const timeout = option(process.env.TIMEOUT); const debug = option(process.env.DEBUG); return { apiKey: apiKey.isSome ? apiKey.value : "default-key", timeout: timeout.isSome ? parseInt(timeout.value) : 3000, debug: debug.isSome && debug.value === "true" }; } const config = getConfig(); console.log("Config:", config); ``` ### Expect - Unwrap with Custom Error Message Unwraps Option or Result, throwing an error with custom message if it fails. Use when failure is truly unrecoverable. ```typescript import { option, Ok, Err } from "slang-ts"; // Unwrap Some successfully const age = option(25).expect("Person must have an age"); console.log("Age:", age); // 25 // Unwrap Ok successfully const config = Ok({ port: 3000 }).expect("Config must be valid"); console.log("Port:", config.port); // 3000 // This throws Error: "Person must have an age" // const invalidAge = option(null).expect("Person must have an age"); // This throws Error: "Config must load successfully" // const invalidConfig = Err("missing file").expect("Config must load successfully"); // Real-world example: loading critical resources function loadApp() { const apiKey = option(process.env.API_KEY).expect("API_KEY environment variable is required"); const dbUrl = option(process.env.DATABASE_URL).expect("DATABASE_URL must be configured"); return { apiKey, dbUrl, ready: true }; } // Only call this during startup where failure should crash the app const app = loadApp(); console.log("App initialized:", app.ready); ``` ### Unwrap/Else - Safe Unwrapping with Fallback Chainable unwrapping that requires a fallback value. Must always call `.else()` to provide a default. ```typescript import { option } from "slang-ts"; // Basic unwrap with fallback const port = option(process.env.PORT).unwrap().else(3000); console.log("Using port:", port); // 3000 if PORT not set // Function fallback for computed defaults const retries = option(process.env.MAX_RETRIES) .unwrap() .else(() => { console.log("No MAX_RETRIES set, using default"); return 5; }); console.log("Retries:", retries); // Handling false as valid value const debugMode = option(false).unwrap().else(true); console.log("Debug mode:", debugMode); // false (not the fallback!) // Configuration with multiple fallbacks function getServerConfig() { const host = option(process.env.HOST).unwrap().else("localhost"); const port = option(process.env.PORT).unwrap().else(() => 8080); const workers = option(process.env.WORKERS).unwrap().else(4); return { host, port, workers }; } const config = getServerConfig(); console.log("Server config:", config); // THIS THROWS - must chain .else() // const invalid = option(null).unwrap(); // console.log(invalid); // Error: "Expected else" ``` ### Atom - Unique Non-Interned Symbols Creates unique symbols with semantic descriptions. Each call produces a distinct identity, perfect for enums or state machines. ```typescript import { atom } from "slang-ts"; // Create unique atoms const ready = atom("ready"); const loading = atom("loading"); const error = atom("error"); // Each atom is unique even with same description const state1 = atom("ready"); const state2 = atom("ready"); console.log(state1 === state2); // false - different atoms! console.log(state1 === ready); // false - different atoms! // Use for type-safe state machines type AppState = typeof ready | typeof loading | typeof error; let currentState: AppState = loading; function setState(newState: AppState) { currentState = newState; console.log("State changed to:", newState.description); } setState(ready); // "State changed to: ready" setState(loading); // "State changed to: loading" setState(error); // "State changed to: error" // Define atoms once and export for app-wide use // constants.ts export const HTTP_METHODS = { GET: atom("GET"), POST: atom("POST"), PUT: atom("PUT"), DELETE: atom("DELETE") } as const; // Use imported atoms for identity checks import { HTTP_METHODS } from "./constants"; function handleRequest(method: typeof HTTP_METHODS[keyof typeof HTTP_METHODS]) { console.log("Handling:", method.description); if (method === HTTP_METHODS.GET) { console.log("Performing GET request"); } else if (method === HTTP_METHODS.POST) { console.log("Performing POST request"); } } handleRequest(HTTP_METHODS.GET); ``` ### Match - Exhaustive Pattern Matching Pattern matching for Result and Option types. Forces handling of all possible cases at compile time. ```typescript import { match, option, Ok, Err } from "slang-ts"; // Matching Result function divide(a: number, b: number) { if (b === 0) return Err("Division by zero"); return Ok(a / b); } const result = divide(10, 2); match(result, { Ok: (v) => console.log("Result:", v.value), // "Result: 5" Err: (e) => console.log("Error:", e.error), }); const errorResult = divide(10, 0); match(errorResult, { Ok: (v) => console.log("Result:", v.value), Err: (e) => console.log("Error:", e.error), // "Error: Division by zero" }); // Matching Option with return values const maybePort = option(process.env.PORT); const port = match(maybePort, { Some: (v) => parseInt(v.value), None: () => 3000, }); console.log("Using port:", port); // Error handling in async operations async function loadUser(id: string) { const result = await fetchUser(id); return match(result, { Ok: (v) => { console.log("Loaded user:", v.value.name); return v.value; }, Err: (e) => { console.error("Failed to load user:", e.error); return null; }, }); } const user = await loadUser("123"); if (user) { console.log("User email:", user.email); } ``` ### MatchAll - Primitive and Atom Pattern Matching Pattern matching for strings, numbers, booleans, and atoms. Requires a `_` fallback case for unmatched values. ```typescript import { matchAll, atom } from "slang-ts"; // Match atoms const ready = atom("ready"); const loading = atom("loading"); const failed = atom("failed"); let state = ready; matchAll(state, { ready: () => console.log("Application is ready!"), loading: () => console.log("Loading..."), failed: () => console.log("Failed to load"), _: () => console.log("Unknown state"), }); // Output: "Application is ready!" // Match booleans const isActive = true; matchAll(isActive, { true: () => console.log("Service is active"), false: () => console.log("Service is inactive"), _: () => console.log("Unknown status"), }); // Output: "Service is active" // Match numbers for HTTP status codes const statusCode = 404; const message = matchAll(statusCode, { 200: () => "OK", 201: () => "Created", 400: () => "Bad Request", 404: () => "Not Found", 500: () => "Internal Server Error", _: () => "Unknown Status", }); console.log("Status:", message); // "Status: Not Found" // Match strings for routing const route = "/api/users"; matchAll(route, { "/": () => console.log("Home page"), "/api/users": () => console.log("Users endpoint"), "/api/posts": () => console.log("Posts endpoint"), _: () => console.log("404 - Route not found"), }); // Output: "Users endpoint" // Complex state machine example type Status = "idle" | "pending" | "success" | "error"; function handleStatus(status: Status, value: string) { matchAll(status, { idle: () => console.log("Waiting to start..."), pending: () => console.log("Processing:", value), success: () => console.log("Completed:", value), error: () => console.log("Failed:", value), _: () => console.log("Unknown status:", status), }); } handleStatus("pending", "user-data"); // "Processing: user-data" handleStatus("success", "user-data"); // "Completed: user-data" ``` ### AndThen - Chainable Transformations Transforms the inner value while preserving the wrapper type. Works with Option, Result, and Atom types. ```typescript import { option, Ok, Err, atom } from "slang-ts"; // Option andThen - transforms Some, skips None const result1 = option(5).andThen(x => x * 2); console.log(result1.value); // 10 const result2 = option(null).andThen(x => x * 2); console.log(result2.type); // "None" - transformation skipped // Returning undefined preserves original value const result3 = option(5).andThen(() => undefined); console.log(result3.value); // 5 - original value preserved // Result andThen - transforms Ok, skips Err const okResult = Ok(10).andThen(x => x + 5); console.log(okResult.value); // 15 const errResult = Err("failed").andThen(x => x + 5); console.log(errResult.error); // "failed" - transformation skipped // Chained transformations const chained = option(5) .andThen(x => x + 1) // 6 .andThen(x => x * 2) // 12 .andThen(x => x.toString()); // "12" console.log(chained.value); // "12" // Type transformation const numberToObject = Ok(42) .andThen(x => x * 2) // 84 .andThen(x => ({ value: x })); // { value: 84 } console.log(numberToObject.value); // { value: 84 } // Atom andThen - transforms description (sync only) const atomResult = atom("hello") .andThen(s => s.toUpperCase()) // "HELLO" .andThen(s => s + "!"); // "HELLO!" console.log(atomResult.description); // "HELLO!" // Async support for Option and Result const asyncResult = await option(5).andThen(async x => { await new Promise(resolve => setTimeout(resolve, 100)); return x * 10; }); console.log(asyncResult.value); // 50 // Error handling - throws in andThen return None/Err const errorHandling1 = option(5).andThen(() => { throw new Error("oops"); }); console.log(errorHandling1.type); // "None" const errorHandling2 = Ok(5).andThen(() => { throw new Error("oops"); }); console.log(errorHandling2.error); // "oops" // Real-world example: data transformation pipeline interface UserDTO { id: string; full_name: string; } interface User { id: string; firstName: string; lastName: string; } function parseUserDTO(dto: UserDTO): Result { return Ok(dto) .andThen(d => { const [firstName, lastName] = d.full_name.split(" "); if (!firstName || !lastName) throw new Error("Invalid name format"); return { id: d.id, firstName, lastName }; }); } const userDto: UserDTO = { id: "123", full_name: "John Doe" }; const user = parseUserDTO(userDto); if (user.isOk) { console.log("Parsed user:", user.value); // { id: "123", firstName: "John", lastName: "Doe" } } ``` ### To - Type Conversion Between Slang Types Converts between Option, Result, and Atom types while preserving values and handling edge cases. ```typescript import { option, atom, Ok, Err } from "slang-ts"; // Atom to Option const statusAtom = atom("active"); const statusOption = statusAtom.to("option"); console.log(statusOption.type); // "Some" console.log(statusOption.value); // "active" // Option to Atom (must be Some with string value) const stateOption = option("ready"); const stateAtom = stateOption.to("atom"); console.log(stateAtom.description); // "ready" // Option to Result const someOption = option("data").to("result"); console.log(someOption.type); // "Ok" console.log(someOption.value); // "data" const noneOption = option(null).to("result"); console.log(noneOption.type); // "Err" console.log(noneOption.error); // "Value is None" // Atom to Result const configAtom = atom("production"); const configResult = configAtom.to("result"); console.log(configResult.type); // "Ok" console.log(configResult.value); // "production" // Identity conversions (returns same instance) const opt = option("test"); const sameOpt = opt.to("option"); console.log(opt === sameOpt); // true const atm = atom("state"); const sameAtm = atm.to("atom"); console.log(atm === sameAtm); // true // Real-world: converting between types in a pipeline function processConfig(envVar: string | undefined) { // Start with Option const maybeValue = option(envVar); // Convert to Result for further processing const result = maybeValue.to("result"); if (result.isOk) { // Convert to Atom for use as state identifier const stateAtom = option(result.value).to("atom"); console.log("Config state:", stateAtom.description); return stateAtom; } else { console.error("Config error:", result.error); return atom("default"); } } const state = processConfig(process.env.APP_ENV); console.log("Application state:", state.description); ``` ### SafeTry - Safe Exception Wrapping Wraps potentially throwing functions in try-catch, returning a Result. Always async, supports both sync and async functions. ```typescript import { safeTry } from "slang-ts"; // Wrap synchronous code const result1 = await safeTry(() => { return "Hello, Slang!"; }); if (result1.isOk) { console.log("Result:", result1.value); // "Hello, Slang!" } // Handle division by zero const result2 = await safeTry(() => { const num = 10; const denom = 0; if (denom === 0) throw new Error("Cannot divide by zero"); return num / denom; }); if (result2.isErr) { console.log("Error:", result2.error); // "Cannot divide by zero" } // Async function support async function fetchUserData(id: string) { const response = await fetch(`https://api.example.com/users/${id}`); if (!response.ok) throw new Error("User not found"); return response.json(); } const asyncResult = await safeTry(() => fetchUserData("123")); if (asyncResult.isOk) { console.log("User data:", asyncResult.value); } else { console.log("Failed to fetch:", asyncResult.error); } // Re-throw critical errors with { throw: true } try { await safeTry(() => { throw new Error("Critical failure!"); }, { throw: true }); } catch (e) { console.log("Caught critical error:", (e as Error).message); } // JSON parsing with error handling async function parseJSON(jsonString: string) { return await safeTry(() => JSON.parse(jsonString)); } const valid = await parseJSON('{"name": "John"}'); if (valid.isOk) { console.log("Parsed:", valid.value.name); // "John" } const invalid = await parseJSON('{invalid json}'); if (invalid.isErr) { console.log("Parse error:", invalid.error); // JSON parse error message } // File operations async function readConfig(path: string) { return await safeTry(async () => { const fs = await import("fs/promises"); const content = await fs.readFile(path, "utf-8"); return JSON.parse(content); }); } const config = await readConfig("./config.json"); if (config.isOk) { console.log("Config loaded:", config.value); } else { console.log("Failed to load config:", config.error); } ``` ### Pipe - Sequential Function Composition Creates pipelines where each function receives a Result and returns a Result. Supports callbacks, error handling, and allows recovery. ```typescript import { pipe, Ok, Err, option, type Result } from "slang-ts"; // Define pipeline functions (take Result, return Result) const add = (x: number) => (res: Result) => res.isOk ? Ok(res.value + x) : res; const multiply = (x: number) => (res: Result) => res.isOk ? Ok(res.value * x) : res; const subtract = (x: number) => (res: Result) => res.isOk ? Ok(res.value - x) : res; // Basic pipeline with plain initial value const basicResult = await pipe( 5, add(3), // 5 + 3 = 8 multiply(2), // 8 * 2 = 16 subtract(1) // 16 - 1 = 15 ).run(); if (basicResult.isOk) { console.log("Result:", basicResult.value); // 15 } // Pipeline with Option as initial value const optionResult = await pipe( option(10), add(5), // 10 + 5 = 15 multiply(3) // 15 * 3 = 45 ).run(); if (optionResult.isOk) { console.log("Option result:", optionResult.value); // 45 } // Pipeline with callbacks to track execution const withCallbacks = await pipe(7, add(3), multiply(2)).run({ onEach: ({ prevResult, currentFn, nextFn }) => { console.log(`After ${currentFn}:`, prevResult.isOk ? prevResult.value : "error"); if (nextFn) console.log(`Next: ${nextFn}`); }, onSuccess: (value) => console.log("Pipeline completed with:", value), onError: (err) => console.log("Pipeline failed:", err.message), }); // Output: // After add: 10 // Next: multiply // After multiply: 20 // Pipeline completed with: 20 // Error handling in pipeline const validate = (max: number) => (res: Result) => { if (res.isErr) return res; if (res.value > max) return Err(`Value ${res.value} exceeds maximum ${max}`); return Ok(res.value); }; const errorResult = await pipe( 15, validate(10), // This will fail add(5) // This won't execute ).run({ onError: (err) => console.log("Error:", err.message), allowErrors: false, // Stop on first error (default) }); if (errorResult.isErr) { console.log("Pipeline failed:", errorResult.error); // "Value 15 exceeds maximum 10" } // Continue on error with allowErrors: true const recoverResult = await pipe( 20, validate(10), // Returns Err (res) => { console.log("Received:", res.type); // "Err" // Recovery: convert Err back to Ok with default value return res.isErr ? Ok(0) : res; }, add(10) // Now works with recovered value: 0 + 10 = 10 ).run({ allowErrors: true }); if (recoverResult.isOk) { console.log("Recovered result:", recoverResult.value); // 10 } // Real-world example: data processing pipeline interface RawUser { name: string; age: string; } interface User { name: string; age: number; } const parseUser = (res: Result) => { if (res.isErr) return res; const age = parseInt(res.value.age); if (isNaN(age)) return Err("Invalid age format"); return Ok({ name: res.value.name, age }); }; const validateAge = (res: Result) => { if (res.isErr) return res; if (res.value.age < 0 || res.value.age > 150) { return Err("Age out of valid range"); } return Ok(res.value); }; const rawUser: RawUser = { name: "Alice", age: "25" }; const userResult = await pipe( rawUser, parseUser, validateAge ).run(); if (userResult.isOk) { console.log("Valid user:", userResult.value); // { name: "Alice", age: 25 } } ``` ### Zip - Combine Collections Element-Wise Combines multiple arrays, Sets, or objects into tuples by index position. Stops at shortest by default or uses fillValue to extend. ```typescript import { zip } from "slang-ts"; // Basic array zipping const arr1 = [1, 2, 3]; const arr2 = [4, 5, 6]; const arr3 = [7, 8, 9]; const zipped = zip([arr1, arr2, arr3]); console.log(zipped); // [[1, 4, 7], [2, 5, 8], [3, 6, 9]] // Zip with different lengths (stops at shortest) const short = [1, 2]; const long = [10, 20, 30, 40]; const zippedShort = zip([short, long]); console.log(zippedShort); // [[1, 10], [2, 20]] // Zip with fillValue to extend to longest const withFill = zip([short, long], { fillValue: 0 }); console.log(withFill); // [[1, 10], [2, 20], [0, 30], [0, 40]] // Zip Sets (requires includeValues: true) const set1 = new Set([10, 20, 30]); const set2 = new Set([100, 200, 300]); const zippedSets = zip([set1, set2], { includeValues: true }); console.log(zippedSets); // [[10, 100], [20, 200], [30, 300]] // Zip objects (requires includeValues: true) const obj1 = { a: 1, b: 2, c: 3 }; const obj2 = { x: 100, y: 200, z: 300 }; const zippedObjs = zip([obj1, obj2], { includeValues: true }); console.log(zippedObjs); // [[1, 100], [2, 200], [3, 300]] // Real-world: combining data from multiple sources const userIds = ["user1", "user2", "user3"]; const userNames = ["Alice", "Bob", "Charlie"]; const userAges = [25, 30, 35]; const users = zip([userIds, userNames, userAges]).map(([id, name, age]) => ({ id, name, age })); console.log(users); // [ // { id: "user1", name: "Alice", age: 25 }, // { id: "user2", name: "Bob", age: 30 }, // { id: "user3", name: "Charlie", age: 35 } // ] // Parallel iteration of related data const timestamps = [100, 200, 300]; const events = ["login", "update", "logout"]; const usernames = ["alice", "bob", "alice"]; zip([timestamps, events, usernames]).forEach(([time, event, user]) => { console.log(`[${time}] ${user}: ${event}`); }); // [100] alice: login // [200] bob: update // [300] alice: logout ``` ### ZipWith - Combine and Transform Collections Combines collections element-wise and applies a transformation function to each tuple. ```typescript import { zipWith } from "slang-ts"; // Sum corresponding elements const arr1 = [1, 2, 3]; const arr2 = [4, 5, 6]; const arr3 = [7, 8, 9]; const sums = zipWith([arr1, arr2, arr3], (tuple) => tuple.reduce((sum, x) => sum + x, 0) ); console.log(sums); // [12, 15, 18] // [1+4+7, 2+5+8, 3+6+9] // Multiply corresponding elements const products = zipWith([arr1, arr2], (tuple) => tuple[0] * tuple[1]); console.log(products); // [4, 10, 18] // [1*4, 2*5, 3*6] // With fillValue for different lengths const short = [1, 2]; const long = [10, 20, 30]; const withDefault = zipWith( [short, long], (t) => t.reduce((sum, x) => sum + x, 0), { fillValue: 0 } ); console.log(withDefault); // [11, 22, 30] // [1+10, 2+20, 0+30] // String concatenation const firstNames = ["John", "Jane", "Bob"]; const lastNames = ["Doe", "Smith", "Johnson"]; const fullNames = zipWith( [firstNames, lastNames], ([first, last]) => `${first} ${last}` ); console.log(fullNames); // ["John Doe", "Jane Smith", "Bob Johnson"] // Calculate statistics from multiple data series const sales2021 = [100, 150, 200]; const sales2022 = [120, 180, 220]; const sales2023 = [140, 200, 250]; const averages = zipWith( [sales2021, sales2022, sales2023], (values) => values.reduce((sum, v) => sum + v, 0) / values.length ); console.log("Average sales by quarter:", averages); // [120, 176.67, 223.33] // Complex object transformations const ids = [1, 2, 3]; const names = ["Alice", "Bob", "Charlie"]; const scores = [95, 87, 92]; const students = zipWith( [ids, names, scores], ([id, name, score]) => ({ id, name, score, grade: score >= 90 ? "A" : "B" }) ); console.log(students); // [ // { id: 1, name: "Alice", score: 95, grade: "A" }, // { id: 2, name: "Bob", score: 87, grade: "B" }, // { id: 3, name: "Charlie", score: 92, grade: "A" } // ] ``` ### Unzip - Separate Tuples Back to Arrays Reverses the zip operation, transforming an array of tuples back into separate arrays. ```typescript import { unzip, zip } from "slang-ts"; // Basic unzip const zipped = [[1, "a"], [2, "b"], [3, "c"]]; const [numbers, letters] = unzip(zipped); console.log(numbers); // [1, 2, 3] console.log(letters); // ["a", "b", "c"] // Round-trip: zip then unzip const arr1 = [1, 2, 3]; const arr2 = [4, 5, 6]; const combined = zip([arr1, arr2]); console.log(combined); // [[1, 4], [2, 5], [3, 6]] const separated = unzip(combined); console.log(separated); // [[1, 2, 3], [4, 5, 6]] console.log(separated[0]); // [1, 2, 3] - same as arr1 console.log(separated[1]); // [4, 5, 6] - same as arr2 // Unzip with multiple columns const data = [ [1, "Alice", 25], [2, "Bob", 30], [3, "Charlie", 35] ]; const [ids, names, ages] = unzip(data); console.log("IDs:", ids); // [1, 2, 3] console.log("Names:", names); // ["Alice", "Bob", "Charlie"] console.log("Ages:", ages); // [25, 30, 35] // Real-world: parsing CSV-like data const csvRows = [ ["2024-01-01", "100", "completed"], ["2024-01-02", "150", "pending"], ["2024-01-03", "200", "completed"] ]; const [dates, amounts, statuses] = unzip(csvRows); console.log("All dates:", dates); console.log("All amounts:", amounts); console.log("All statuses:", statuses); // Separate coordinates const points = [ [10, 20], [30, 40], [50, 60] ]; const [xCoords, yCoords] = unzip(points); console.log("X coordinates:", xCoords); // [10, 30, 50] console.log("Y coordinates:", yCoords); // [20, 40, 60] ``` ### Panic - Unrecoverable Error Throwing Immediately throws an error for unrecoverable failures. Use for invariant violations or critical failures. ```typescript import { panic } from "slang-ts"; // Guard against null values function processUser(user: User | null) { if (!user) { panic("User cannot be null"); } return user.name.toUpperCase(); } const user = { name: "Alice" }; console.log(processUser(user)); // "ALICE" // This throws Error: "User cannot be null" // processUser(null); // Configuration validation at startup function loadConfig() { const config = { apiKey: process.env.API_KEY, dbUrl: process.env.DATABASE_URL, port: process.env.PORT }; if (!config.apiKey) { panic("API_KEY environment variable is required"); } if (!config.dbUrl) { panic("DATABASE_URL environment variable is required"); } return { apiKey: config.apiKey, dbUrl: config.dbUrl, port: config.port || "3000" }; } // Only runs if all critical config is present const config = loadConfig(); console.log("App configured:", config); // Invariant checking in algorithms function divide(a: number, b: number): number { if (b === 0) { panic("Division by zero is undefined"); } return a / b; } console.log(divide(10, 2)); // 5 // divide(10, 0); // throws Error: "Division by zero is undefined" // Index bounds checking function getElement(array: T[], index: number): T { if (index < 0 || index >= array.length) { panic(`Index ${index} out of bounds for array of length ${array.length}`); } return array[index]; } const numbers = [10, 20, 30]; console.log(getElement(numbers, 1)); // 20 // getElement(numbers, 5); // throws Error: "Index 5 out of bounds..." // State machine invariants type State = "idle" | "loading" | "success" | "error"; function transition(current: State, action: string): State { if (current === "success" && action === "start") { panic("Cannot start from success state without reset"); } // Normal transitions... if (action === "start") return "loading"; if (action === "complete") return "success"; if (action === "fail") return "error"; return current; } let state: State = "idle"; state = transition(state, "start"); // "loading" state = transition(state, "complete"); // "success" // transition(state, "start"); // throws Error: "Cannot start from success state..." ``` ### Println - Enhanced Console Logging Simple console.log wrapper with cleaner syntax. Supports multiple arguments and objects. ```typescript import { println } from "slang-ts"; // Basic usage const name = "Alice"; println("name:", name); // Output: name: Alice // Multiple arguments println("multiple", "args", "work", { too: true }); // Output: multiple args work { too: true } // Objects and arrays const user = { id: 1, name: "Bob", active: true }; println("User data:", user); // Output: User data: { id: 1, name: "Bob", active: true } const numbers = [1, 2, 3, 4, 5]; println("Numbers:", numbers); // Output: Numbers: [1, 2, 3, 4, 5] // Debug logging in functions function processOrder(orderId: string) { println("Processing order:", orderId); const order = { id: orderId, status: "processing" }; println("Order state:", order); order.status = "completed"; println("Order completed:", orderId); return order; } const result = processOrder("ORDER-123"); // Output: // Processing order: ORDER-123 // Order state: { id: "ORDER-123", status: "processing" } // Order completed: ORDER-123 // Variable inspection const port = 3000; const host = "localhost"; const debug = true; println("Server config:", { port, host, debug }); // Output: Server config: { port: 3000, host: "localhost", debug: true } ``` ## Summary Slang provides a comprehensive toolkit for writing robust TypeScript applications with explicit error handling and functional programming patterns. The library's core abstractions—Result for fallible operations, Option for nullable values, and Atom for unique identifiers—eliminate entire classes of runtime errors by making them impossible to represent in the type system. The pattern matching utilities ensure exhaustive case handling, while the transformation and composition tools enable building complex data processing pipelines with clear error propagation semantics. All utilities are immutable by default and preserve type information through transformations. Integration with existing TypeScript codebases is straightforward through individual imports or namespace imports. The library works seamlessly with async/await patterns, supports both synchronous and asynchronous operations, and provides conversion utilities for moving between different wrapper types. Common use cases include API error handling, configuration management with environment variables, data validation pipelines, state machines with compile-time guarantees, and defensive programming patterns that prevent null reference errors. The library has zero runtime dependencies and compiles to standard JavaScript for universal compatibility across Node.js, Deno, Bun, and browser environments.