Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/elfrask/cls/llms.txt

Use this file to discover all available pages before exploring further.

Functions are first-class values in CLS. They can be declared with full type signatures, assigned to variables, passed as arguments, returned from other functions, and composed into async pipelines. CLS supports named functions, arrow function expressions, generic functions with type inference, and async/await for deferred, concurrent workflows. Every function body is a block that closes with };, and every statement inside that block ends with ;.

Named Function Declaration

Use the function keyword to declare a named function with typed parameters and an explicit return type. The return type follows the -> arrow after the parameter list.
function add(a: int, b: int) -> int {
    return a + b;
};

function greet(name: String) -> String {
    return "Hello, " + name + "!";
};

function main(args: String[]) -> int {
    print(add(3, 7));           # → 10
    print(greet("CLS"));        # → Hello, CLS!
    return 0;
};

Void Functions

A function with no meaningful return value is a procedure. Omit the -> Type annotation, or use the void keyword before the function name. Both forms are equivalent.
# Form 1: omit the return type
function log(message: String) {
    print("[LOG]", message);
};

# Form 2: void keyword
void cleanup() {
    print("cleaning up...");
};
Inside a void function, a bare return; (with no value) exits the function early. This is the idiomatic way to write early exits in procedures.

Default Parameters

Parameters can have default values. If a caller omits that argument, the default is used. Parameters with defaults must come after required parameters.
function greet(name: String, saludo: String = "Hola") -> String {
    return saludo + ", " + name;
};

print(greet("Ana"));            # → Hola, Ana
print(greet("Ana", "Hey"));     # → Hey, Ana

The return Statement

return exits the current function and optionally passes a value back to the caller. In typed functions, the returned expression must be assignable to the declared return type.
function factorial(n: int) -> int {
    if (n <= 1) { return 1; };      # early return
    return n * factorial(n - 1);
};

print(factorial(5));                # → 120
print(factorial(0));                # → 1

Arrow Functions

Arrow functions are anonymous function expressions. They are written as (params) -> expression (expression form) or (params) -> { statements } (block form). Arrow functions capture their surrounding lexical scope and behave as closures.
# Implicit return — the expression value is the result
var double = (x: int) -> x * 2;
var square = (n: int) -> n * n;
var greet  = (name: String) -> "Hello, " + name;

print(double(5));     # → 10
print(square(4));     # → 16

Functions as Values

Named functions are first-class values. You can assign a declared function to a variable and call it through that variable, or pass it to higher-order functions.
function add(a: int, b: int) -> int {
    return a + b;
};

function square(n: int) -> int {
    return n * n;
};

# Assign to a variable
var f = add;
print(f(1, 2));          # → 3

# Pass as an argument
function applyTwice(fn: (int) -> int, x: int) -> int {
    return fn(fn(x));
};

print(applyTwice(square, 2));   # → 16  (square(square(2)) = square(4) = 16)

Function Type Signatures

The type of a function is written as (Param1, Param2, ...) -> ReturnType. You can use this in type annotations and create named aliases with alias.
alias Operacion  = (Int, Int) -> Int;
alias Predicado  = (Int) -> Bool;
alias Transform  = (String) -> String;

var suma: Operacion  = (a: int, b: int) -> a + b;
var esPar: Predicado = (n: int) -> n % 2 == 0;
var upper: Transform = (s: String) -> s;       # identity — substitute a real transform at call site

Generic Functions

Functions can be parameterized with type variables declared in <> after the function name. The type checker infers the concrete type argument from the arguments passed at the call site.
function id<T>(x: T) -> T {
    return x;
};

function first<T>(arr: T[]) -> T {
    return arr[0];
};

function pair<A, B>(a: A, b: B) -> (A, B) {
    return (a, b);
};

var n: Int    = id(42);          # T inferred as Int
var s: String = id("hello");     # T inferred as String
var p = pair(1, "one");          # (Int, String)
Type arguments are inferred at the call site — you do not need to write id<Int>(42). The type checker resolves T from the argument type and propagates it through the return type.

Async / Await

CLS supports asynchronous programming through async function and the await expression. An async function returns a Promise immediately when called; its body is deferred until the Promise is consumed. await suspends the current async context until a Promise resolves and returns its value.
import "http" as http;

async function fetchPage(url: String) -> String {
    var response = await http.get(url);
    return response;
};

async function main(args: String[]) -> int {
    var html = await fetchPage("https://example.com");
    print("Received", len(html), "bytes");
    return 0;
};
async function creates a coroutine. Calling it does not execute the body immediately — it returns a Promise. The body runs only when the Promise is awaited or otherwise consumed. This means top-level async code should itself be inside an async function main.

Recursion

Functions can call themselves. The type checker validates recursive calls the same way as any other call — the parameter and return types must be consistent.
function factorial(n: int) -> int {
    if (n <= 1) { return 1; };
    return n * factorial(n - 1);
};

function fibonacci(n: int) -> int {
    if (n <= 1) { return n; };
    return fibonacci(n - 1) + fibonacci(n - 2);
};

print(factorial(10));    # → 3628800
print(fibonacci(10));    # → 55

Visibility Modifiers

Functions support visibility and scope modifiers. These are meaningful for module exports and class members.
ModifierContextDescription
exportModule-levelMakes the function available when the module is imported
publicClass memberAccessible from outside the class (default)
privateClass memberAccessible only within the class body
protectedClass memberAccessible within the class and subclasses
staticClass memberBelongs to the class, not an instance; no me access
# Module-level export
export function publicApi(x: int) -> int {
    return x * 2;
};

# Class with mixed visibility
class Wallet {
    private var balance: int = 0;

    public function deposit(amount: int) {
        me.balance = me.balance + amount;
    };

    public function getBalance() -> int {
        return me.balance;
    };

    static function create() -> Wallet {
        return Wallet();
    };
};
# math-utils.clsx
export function clamp(val: int, lo: int, hi: int) -> int {
    if (val < lo) { return lo; };
    if (val > hi) { return hi; };
    return val;
};

export function lerp(a: float, b: float, t: float) -> float {
    return a + (b - a) * t;
};
# main.clsx
from "math-utils" import clamp, lerp;

print(clamp(15, 0, 10));        # → 10
print(lerp(0.0, 100.0, 0.25));  # → 25.0

Build docs developers (and LLMs) love