Functions

    Functions in Cyrus are defined using the fn keyword, followed by the function name, parameters, and return type.


    Basic Function Example

    Here's a simple example of a function that adds two integers:

    fn sum(x: int, y: int) int {
        return x + y;
    }
    

    Recursion

    fn main() {
        fibonacci(10); // expected value: 55
    }
    
    fn fibonacci(n: int) int {
        if (n == 0) {
            return 0;
        }
        else if (n == 1) {
            return 1;
        }
        else {
            return fibonacci(n - 1) + fibonacci(n - 2);
        }
    }
    

    Function Access Specifiers

    In Cyrus, functions can be declared with access specifiers that control their visibility and behavior.
    These specifiers are placed before the fn keyword.

    • extern - Declares that the function is defined outside the current module (e.g., in a C library). Useful for interoperability.
    extern fn printf(format: const char*, ...) int32;
    

    Only valid for declarations.

    • pub Makes the function visible outside the current module. Without pub, a function is private by default and can only be used within the same module.
    pub fn sum(a: int, b: int) int {
        return a + b;
    }
    
    • inline Hints to the compiler that the function body should be expanded inline at call sites (for performance). This is most effective with small utility functions.
    inline fn square(x: int) int {
        return x * x;
    }
    
    • pub inline A combination: visible outside the module and a candidate for inlining.
    pub inline fn abs(x: int) int {
        if (x < 0) {
            return -x;
        } else {
            return x;
        }
    }
    
    • pub extern Declares an external function that is also exported from the current module. This is commonly used when exposing bindings or APIs.
    pub extern fn sin(x: float64) float64;
    

    Variadic Arguments

    Cyrus supports two kinds of variadic arguments, allowing functions to accept a variable number of parameters.


    C-style Variadic Arguments

    C-style variadic functions use the ... notation, just like in C. These are typically used for interfacing with C libraries (e.g., printf) or when type safety is not a concern.

    extern fn printf(format: const char*, ...) int;
    
    fn main() {
        printf("Hello %s, number = %d\n", "Cyrus", 42);
    }
    
    • Use this style primarily for compatibility with C and low-level APIs.
    • Inside the function body, such arguments are accessed with platform-specific varargs handling (similar to C's va_list).

    Typed Variadic Arguments (Not implemented yet.)

    Typed variadic functions allow specifying one type that repeats for an arbitrary number of arguments. This gives type safety and better compiler optimizations.

    fn sum_all(nums: int...) int {
        // Coming soon.
    }
    
    fn main() {
        var s: int = sum_all(1, 2, 3, 4, 5);
        printf("%d\n", s); // Output: 15
    }
    

    Function ABI Name

    Cyrus allows you to specify a different ABI-level (linker) name for a function than its Cyrus name. This is done using the as keyword after the declaration.

    extern fn printf(fmt: string, ...) int as my_printf;
    
    fn main() {
        my_printf("Hello, %s!\n", "Cyrus");
    }
    
    • The function is declared as printf at the ABI level, so it links against the real printf symbol in the C standard library.
    • Inside Cyrus, however, the function is referred to as my_printf.
    • This allows you to avoid naming conflicts or to wrap existing library functions under more descriptive names.

    Lambdas

    Lambdas in Cyrus are anonymous functions. They look and behave like normal functions, but they don't need a name. You define them inline, assign them to variables, pass them around, or embed them inside structs.

    Unlike closures in some other languages, Cyrus lambdas do not capture the surrounding environment. They are pure function objects defined with a clear, fixed signature.


    Function Types

    Every lambda has a function type, which describes its parameters and return type. For example:

    type HandlerFn = fn(int, int) void;
    

    Defining a Lambda

    The syntax is straightforward:

    fn(x: int, y: int) int {
        return x + y;
    }
    

    Assigning to Variables

    You can store lambdas in variables:

    fn main() {
        var add = fn(x: int, y: int) int {
            return x + y;
        };
    
        printf("%d\n", add(2, 3)); // prints 5
    }
    

    No Environment Capture

    In Cyrus, lambdas are not closures. That means they cannot capture variables from their surrounding scope. For example:

    fn main() {
        var base = 10;
    
        var adder = fn(x: int) int {
            return x + base; // ❌ base is not accessible here
        };
    }