5 Functions


5. Functions

Functions are user-defined procedures that encapsulate reusable code. They are the primary abstraction mechanism in the language.

5.1 What are Functions

Functions differ from operators:

Related: See Section 1 "Operators vs Functions" for a complete explanation of the distinction.

5.2 Defining Functions

Syntax: (inputs -- outputs) { body } ::name fn

The function signature specifies stack effects (what is consumed and produced), the body contains the implementation, and the name identifies the function.

Examples:

// Simple function - requires Multiplyable trait
(Multiplyable -- Multiplyable) { dup * } ::square fn

// Use it
5 square      // => 25

// Multiple inputs and outputs
(Number Number -- Number Number) {
    over over / swap %
} ::divmod fn

10 3 divmod   // => 3 1 (quotient remainder)

Function Bodies: The { } braces contain a TokenString that is parsed as the function body when the function is defined. See Section 5.5 for details on TokenStrings.

Related: See Section 10.2 for generic functions with type parameters.

5.3 Calling Functions

Functions are called by simply writing their name. The postfix notation means arguments must be on the stack before the function name:

// Define a function
(Number Number -- Number) { + } ::add fn

// Call it
3 4 add       // => 7

// Chain functions
5 square 2 * // => 50 (square 5, then multiply by 2)

Function Chaining: Since everything is postfix, functions naturally chain left-to-right:

10 square 2 / 5 +    // ((10²) / 2) + 5 = 55

5.4 Recursion

Functions can call themselves recursively:

(Number -- Number) {
    dup 1 <=
        { drop 1 }
        { dup 1 - factorial * }
    if
} ::factorial fn

5 factorial    // => 120

Stack Considerations: Recursive functions consume stack space. Deep recursion may cause stack overflow. Consider iterative alternatives for performance-critical code.

5.5 Token Strings

Token strings are lexed but unparsed code blocks enclosed in { }. They are parsed differently depending on which operator uses them.

Operators that parse TokenStrings:

Examples:

// Function body (parsed by fn)
(Number -- Number) { dup * } ::square fn

// Conditional branches (parsed by if)
x 0 > { "positive" print } { "negative" print } if

// Loop body (parsed by while)
{ dup 10 < } { dup print 1 + } while

5.6 Lambda Functions

The lambda operator converts a TokenString into a callable code block that can be stored and passed around:

{ dup * } lambda        // Creates a callable code block
::square_fn swap        // Store reference to the lambda

// Later use:
5 square_fn eval        // Calls the lambda: pushes 25

Use Cases:

Example with arrays:

// Store a lambda and use it with map
{ 2 * } lambda ::double swap
[1 2 3 4] double map    // => [2 4 6 8]

Related: See Section 11.1 for the eval operator used to execute lambdas.