Skip to content

Functions

Language: Español | English


Functions in Crespi allow you to encapsulate and reuse code.

Basic Declaration

Use fn to define a function with type annotations:

fn greet() {
    print("Hello!")
}

greet()  // Hello!

With Parameters

fn greet(name: String) {
    print("Hello, " + name + "!")
}

greet("Ana")    // Hello, Ana!
greet("Carlos") // Hello, Carlos!

With Multiple Parameters

fn add(a: Int, b: Int) -> Int {
    return a + b
}

print(add(3, 5))   // 8
print(add(10, 20)) // 30

Return Value

Use return to return a value. Specify the return type with ->:

fn square(x: Int) -> Int {
    return x * x
}

var r = square(5)
print(r)  // 25

Early Return

return terminates the function immediately:

fn absolute(n: Int) -> Int {
    if n < 0 {
        return -n
    }
    return n
}

print(absolute(-5))  // 5
print(absolute(3))   // 3

No Explicit Return

If there's no return, the function returns null:

fn greet(name: String) {
    print("Hello, " + name)
}

var r = greet("Ana")
print(r)  // null

Single-Expression Syntax

For simple functions, use the short syntax with fn and =:

// Standard syntax
fn double(x: Int) -> Int {
    return x * 2
}

// Short syntax (equivalent)
fn double(x: Int) -> Int = x * 2

More Examples

// Single expression
fn square(x: Int) -> Int = x * x
fn triple(x: Int) -> Int = x * 3

// Multiple parameters
fn sum(a: Int, b: Int) -> Int = a + b
fn average(a: Float, b: Float) -> Float = (a + b) / 2.0

// No parameters
fn getPi() -> Float = 3.14159
fn greeting() -> String = "Hello world"

// Usage
print(square(4))            // 16
print(sum(10, 5))           // 15
print(average(80.0, 90.0))  // 85.0

Default Parameters

You can assign default values to parameters:

fn greet(name: String = "World") {
    print("Hello, " + name)
}

greet()       // Hello, World
greet("Ana")  // Hello, Ana

Multiple Parameters with Defaults

fn createMessage(text: String, repetitions: Int = 1, separator: String = " ") -> String {
    var message = ""
    var i = 0

    while i < repetitions {
        if i > 0 {
            message = message + separator
        }
        message = message + text
        i += 1
    }

    return message
}

print(createMessage("Hello"))           // Hello
print(createMessage("Hello", 3))        // Hello Hello Hello
print(createMessage("Hello", 3, "-"))   // Hello-Hello-Hello

Functions as Values

Functions are first-class values:

Assign to Variables

fn duplicate(x: Int) -> Int {
    return x * 2
}

var operation = duplicate
print(operation(5))  // 10

Pass as Argument

fn apply(f: (Int) -> Int, value: Int) -> Int {
    return f(value)
}

fn square(x: Int) -> Int = x * x
fn cube(x: Int) -> Int = x * x * x

print(apply(square, 4))  // 16
print(apply(cube, 3))    // 27

Return Functions

fn createMultiplier(factor: Int) -> (Int) -> Int {
    fn multiply(x: Int) -> Int {
        return x * factor
    }
    return multiply
}

var double = createMultiplier(2)
var triple = createMultiplier(3)

print(double(5))   // 10
print(triple(5))   // 15

Closures

Functions capture variables from their environment:

fn createCounter() -> () -> Int {
    var count = 0

    fn increment() -> Int {
        count += 1
        return count
    }

    return increment
}

var counter = createCounter()
print(counter())  // 1
print(counter())  // 2
print(counter())  // 3

Multiple Closures

fn createCounterWithStep(step: Int) -> () -> Int {
    var count = 0

    fn next() -> Int {
        count += step
        return count
    }

    return next
}

var byOne = createCounterWithStep(1)
var byFive = createCounterWithStep(5)

print(byOne())    // 1
print(byOne())    // 2
print(byFive())   // 5
print(byFive())   // 10

Recursion

Functions can call themselves:

Factorial

fn factorial(n: Int) -> Int {
    if n <= 1 {
        return 1
    }
    return n * factorial(n - 1)
}

print(factorial(5))  // 120 (5 * 4 * 3 * 2 * 1)

Fibonacci

fn fibonacci(n: Int) -> Int {
    if n <= 1 {
        return n
    }
    return fibonacci(n - 1) + fibonacci(n - 2)
}

print(fibonacci(10))  // 55

Tail Recursion

For deep recursion, use accumulators:

fn factorialTail(n: Int, acc: Int = 1) -> Int {
    if n <= 1 {
        return acc
    }
    return factorialTail(n - 1, n * acc)
}

print(factorialTail(5))  // 120

Inlining

Hint to the native compiler to inline a function's body at call sites using the @inline decorator. This can improve performance for small, frequently called functions by avoiding function call overhead.

@inline
fn add(a: Int, b: Int) -> Int = a + b

// This call is replaced by the actual addition instruction in the binary
var sum = add(5, 10)

Nested Functions

Define functions inside other functions:

fn calculateTaxes(price: Float) -> Float {
    let RATE = 0.16

    fn applyRate(amount: Float) -> Float {
        return amount * RATE
    }

    var tax = applyRate(price)
    return price + tax
}

print(calculateTaxes(100.0))  // 116.0

Generic Functions

Functions can have type parameters. Type parameters go before the function name:

fn [T] identity(x: T) -> T {
    return x
}

print(identity(42))      // 42
print(identity("hello")) // hello

fn [T, U] transform(value: T, f: (T) -> U) -> U {
    return f(value)
}

var doubled = transform(5, x => x * 2)
print(doubled)  // 10

Common Patterns

Map (Transform List)

fn [T, U] myMap(list: List[T], transform: (T) -> U) -> List[U] {
    var result = []

    for item in list {
        result.push(transform(item))
    }

    return result
}

fn double(x: Int) -> Int = x * 2

var numbers = [1, 2, 3, 4, 5]
print(myMap(numbers, double))  // [2, 4, 6, 8, 10]

Filter

fn [T] myFilter(list: List[T], predicate: (T) -> Bool) -> List[T] {
    var result = []

    for item in list {
        if predicate(item) {
            result.push(item)
        }
    }

    return result
}

fn isEven(n: Int) -> Bool = n % 2 == 0

var numbers = [1, 2, 3, 4, 5, 6]
print(myFilter(numbers, isEven))  // [2, 4, 6]

Reduce (Accumulate)

fn [T, U] myReduce(list: List[T], accumulator: (U, T) -> U, initial: U) -> U {
    var result = initial

    for item in list {
        result = accumulator(result, item)
    }

    return result
}

fn add(a: Int, b: Int) -> Int = a + b

var numbers = [1, 2, 3, 4, 5]
print(myReduce(numbers, add, 0))  // 15

See Also