Fahrenheit 0.4.0

Functions

Defining Functions

Functions are defined using the function keyword. In Fahrenheit 0.4.0, you must specify parameter types. You can also specify an optional return type.


# Typed function (parameter and return types are checked at runtime)
function int multiply(int a, int b) {
    return a * b;
}

# If you want to accept any type, use 'any'
function void greet(any name) {
    print("Hello " + name);
}

product = multiply(4, 3); # 12
greet("World");           # Hello World
    

Optional Parameters (Default Values)

Fahrenheit supports optional parameters with default values. Default value expressions are evaluated at call-time and may reference previously defined parameters.


# Simple default value
function greet(String name, String greeting = "Hello") {
    return greeting +", " + name + "!";
}

print(greet("Alice"));         # Hello, Alice!
print(greet("Bob", "Hi"));     # Hi, Bob!

# Default referencing a previous parameter
function add_offset(int val, int offset = val * 2) {
    return val + offset;
}

print(add_offset(10));         # 30 (10 + 20)
print(add_offset(10, 5));      # 15 (10 + 5)
    

Named Arguments

When calling a function, you can provide arguments by name using the name = value syntax. Named arguments can be in any order and are especially useful for skipping optional parameters.


function config(String host, int port = 8080, boolean secure = false) {
    return host + ":" + port + " (secure=" + secure + ")";
}

# Skip 'port' by naming 'secure'
print(config("localhost", secure = true));
# Output: localhost:8080 (secure=true)

# All named, out of order
print(config(secure = true, host = "127.0.0.1", port = 443));
# Output: 127.0.0.1:443 (secure=true)
    

Variable-Length Arguments (Varargs)

Fahrenheit supports variable-length arguments using the ... syntax. The varargs parameter must be the last one in the list and will be treated as an array inside the function.


function int sum(...int numbers) {
    int total = 0;
    foreach (n : numbers) {
        total += n;
    }
    return total;
}

print(sum(1, 2, 3)); # 6
print(sum(10, 20));  # 30
    

First-Class Functions & Lambdas

Fahrenheit treats functions as first-class citizens. Functions can be assigned to variables, passed as arguments to other functions, and returned from functions. Declare a variable to hold a function using the func or function type. Define anonymous functions using the lambda => syntax.


# Lambda assigned to a variable
func adder = (a, b) => a + b;
print(adder(10, 20)); # 30

# Lambda with a block body
func myFunc = (x, y) => {
    result = x * y;
    return result;
};

# Pass a function as an argument
function int apply(func operation, int a, int b) {
    return operation(a, b);
}
apply((x, y) => x - y, 50, 20); # 30

# Named function references
function multiplyByTwo(int x) { return x * 2; }
func op = multiplyByTwo;  # function name acts as a variable

# Struct methods as function variables
structure Calculator {
    function add(int a, int b) { return a + b; }
}
calc = Calculator();
apply(calc.add, 15, 25); # 40
    

Functional Array Operations

Arrays support map, filter, and reduce operations using function names.


any nums = [1, 2, 3, 4];

function int square(int x) { return x * x; }
function boolean isEven(int x) { return x % 2 == 0; }
function int sum(int acc, int x) { return acc + x; }

any squared = nums.map("square");     # [1, 4, 9, 16]
any evens = nums.filter("isEven");    # [2, 4]
any total = nums.reduce("sum", 0);    # 10
    

Dynamic Method Invocation

The call() function allows calling methods by name using string variable indirection:


function String greet(String name) {
    return "Hello " + name;
}

String methodName = "greet";
any result = call(methodName, "World");  # Returns "Hello World"