Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.syntblaze.com/llms.txt

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

A local function in Kotlin is a function declared directly within the body of another function. It is lexically scoped to its enclosing block, meaning it is entirely invisible and inaccessible from outside the execution context of the outer function.

Syntax

Local functions are declared using the standard fun keyword nested inside an existing function block. They must be declared before they are invoked within the outer function’s execution flow.
fun outerFunction(value: Int) {
    // Declaration of the local function
    fun localFunction(multiplier: Int): Int {
        return value * multiplier
    }

    // Invocation within the enclosing scope
    val result = localFunction(2)
}

Closures and Variable Capture

Local functions form a closure over their lexical environment. This allows the local function to access, capture, and modify local variables and parameters defined in the outer function’s scope. Unlike Java, where captured variables must be effectively final, Kotlin allows local functions to mutate captured var references.
fun processValues(values: List<Int>) {
    var accumulator = 0 // Mutable state in outer scope

    fun accumulate(amount: Int) {
        accumulator += amount // Mutating the captured variable
    }

    for (value in values) {
        accumulate(value)
    }
}

Execution and JVM Mechanics

The Kotlin compiler translates local functions to JVM bytecode using specific optimization strategies depending on how the function interacts with its lexical scope and how it is invoked:
  1. Direct Invocation (Non-capturing): If the local function does not reference any variables from the outer scope, the compiler extracts it into a standard private static method at the class level. This incurs no runtime allocation overhead.
  2. Direct Invocation (Capturing): If the local function captures variables but is only invoked directly within the outer function, the compiler optimizes it by generating a private method. The captured variables are passed to this generated method as additional hidden arguments. No synthetic function object is created, and no heap allocation occurs for the function itself.
  3. Usage as a Value: If the local function is used as an object—such as being passed as a function reference to a higher-order function—the compiler must generate a synthetic class implementing the appropriate FunctionN interface. An instance of this class is then allocated on the heap to hold the captured state.
  4. Mutating Captured Variables: If a local function mutates a captured var, the compiler wraps that variable in a heap-allocated reference object (e.g., IntRef, ObjectRef). Both the outer function and the local function operate on this shared reference to synchronize state mutations. This heap allocation applies strictly to the mutated variable’s wrapper, not the function itself (unless it is also passed as a value).

Recursion

Local functions fully support recursion, including tail recursion via the tailrec modifier. This allows recursive algorithms to be encapsulated without exposing the recursive helper function to the broader class or file scope.
fun computeFactorial(n: Int): Long {
    tailrec fun factorial(current: Int, accumulator: Long): Long {
        if (current <= 1) return accumulator
        return factorial(current - 1, current * accumulator)
    }
    
    return factorial(n, 1L)
}
Master Kotlin with Deep Grasping Methodology!Learn More