Skip to content

Introduction to Functions

Functions are reusable blocks of code that perform a specific task. They are a fundamental building block of Harneet programs and support advanced features like returning other functions, closures, and higher-order programming.

Declaration Syntax

The basic syntax for declaring a function is as follows:

Function Declaration Syntax
1
2
3
4
5
package main
function functionName(param1 type1, param2 type2) returnType {
    // function body
    return value
}
  • function: The keyword to declare a function.
  • functionName: The name of the function.
  • (param1 type1, param2 type2): The list of parameters with their types.
  • returnType: The type of the value returned by the function.
  • { ... }: The body of the function containing the code to be executed.
  • return: The keyword to return a value from the function.

Basic Examples

Function with a single return value

Single Return Value
1
2
3
4
5
6
7
8
9
package main
import fmt

function square(x int) int {
    return x * x
}

var result = square(5)
fmt.Println(result) // Output: 25

Function with no parameters

No Parameters
1
2
3
4
5
6
7
8
9
package main
import fmt

function getAnswer() int {
    return 42
}

var answer = getAnswer()
fmt.Println(answer) // Output: 42

Function with no return value

No Return Value
1
2
3
4
5
6
7
8
package main
import fmt

function printMessage() {
    fmt.Println("Hello, Harneet!")
}

printMessage() // Output: Hello, Harneet!

Returning Functions

Harneet supports returning functions from other functions, enabling powerful functional programming patterns like closures and higher-order functions.

Basic Function Return

Basic Function Return
package main
import fmt

function getGreeter() function(string) string {
    function greet(name string) string {
        return "Hello, " + name + "!"
    }
    return greet
}

var greeter = getGreeter()
var message = greeter("World")
fmt.Println(message) // Output: Hello, World!

Closures with Variable Capture

Functions can capture variables from their enclosing scope, creating closures:

Closure with Variable Capture
package main
import fmt

function createAdder(x int) function(int) int {
    function inner(y int) int {
        return x + y  // Captures 'x' from outer scope
    }
    return inner
}

var add5 = createAdder(5)
var result = add5(3)
fmt.Println(result) // Output: 8

Returning Arrow Functions

You can also return arrow functions for more concise syntax:

Return Arrow Function
package main
import fmt

function createMultiplier(factor int) function(int) int {
    return x => x * factor
}

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

fmt.Printf("Double 5: %d\n", double(5))   // Output: Double 5: 10
fmt.Printf("Triple 4: %d\n", triple(4))  // Output: Triple 4: 12

Higher-Order Functions

Functions can return functions that themselves return functions, enabling complex functional patterns:

Higher-Order Function
package main
import fmt

function createCalculator() function(string) function(int, int) int {
    function getOperation(op string) function(int, int) int {
        if op == "add" {
            return (a int, b int) int => a + b
        } else if op == "subtract" {
            return (a int, b int) int => a - b
        } else {
            return (a int, b int) int => a * b
        }
    }
    return getOperation
}

var calculator = createCalculator()
var addFunc = calculator("add")
var subFunc = calculator("subtract")

fmt.Printf("10 + 5 = %d\n", addFunc(10, 5))  // Output: 10 + 5 = 15
fmt.Printf("10 - 5 = %d\n", subFunc(10, 5))  // Output: 10 - 5 = 5

Conditional Function Returns

Functions can return different functions based on runtime conditions:

Conditional Function Returns
package main
import fmt

function createMathOperation(operation string) function(int, int) int {
    if operation == "add" {
        function add(a int, b int) int {
            return a + b
        }
        return add
    } else {
        function multiply(a int, b int) int {
            return a * b
        }
        return multiply
    }
}

var addOp = createMathOperation("add")
var multiplyOp = createMathOperation("multiply")

fmt.Printf("Add: %d\n", addOp(10, 20))        // Output: Add: 30
fmt.Printf("Multiply: %d\n", multiplyOp(4, 5)) // Output: Multiply: 20

Function Type Annotations

Harneet provides comprehensive support for function type annotations, enabling strong type safety for higher-order functions and functional programming patterns.

Function Parameters with Function Types

You can specify function types as parameters, enabling type-safe higher-order functions:

Function Parameter Types
package main
import fmt

// Function that takes another function as a parameter
function apply(f function(int) int, value int) int {
    return f(value)
}

// Function that takes multiple function parameters
function compose(f function(int) int, g function(int) int, value int) int {
    return f(g(value))
}

// Test functions
function double(x int) int {
    return x * 2
}

function addTen(x int) int {
    return x + 10
}

// Usage with full type validation
var result1 = apply(double, 5)           // Output: 10
var result2 = compose(double, addTen, 5) // Output: 30

fmt.Printf("apply(double, 5) = %d\n", result1)
fmt.Printf("compose(double, addTen, 5) = %d\n", result2)

Function Variables with Type Annotations

Variables can be declared with specific function types:

Function Variable Declaration
package main
import fmt

// Declare a function variable with a specific signature
var handler function(int) int

// Helper function
function increment(x int) int {
    return x + 1
}

// Assign function to typed variable
handler = increment

// Use the function variable
var result = handler(7)
fmt.Printf("handler(7) = %d\n", result) // Output: 8

Multiple Parameter Function Types

Function types can specify multiple parameters and different return types:

Multiple Parameter Function Types
package main
import fmt

// Function expecting a binary operation
function calculate(op function(int, int) int, a int, b int) int {
    return op(a, b)
}

// Function expecting a string transformation
function transform(fn function(string) string, text string) string {
    return fn(text)
}

// Test functions
function add(x int, y int) int {
    return x + y
}

function toUpper(s string) string {
    return "[UPPER]" + s
}

// Usage with type validation
var sum = calculate(add, 10, 5)
var upper = transform(toUpper, "hello")

fmt.Printf("calculate(add, 10, 5) = %d\n", sum)      // Output: 15
fmt.Printf("transform(toUpper, \"hello\") = %s\n", upper) // Output: [UPPER]hello

Arrow Functions with Type Annotations

Arrow functions work seamlessly with function type annotations:

Arrow Functions with Types
package main
import fmt

// Function expecting a function parameter
function processNumbers(processor function(int) int, numbers []int) []int {
    var results []int
    for _, var num = range numbers {
        results = append(results, processor(num))
    }
    return results
}

// Arrow functions can be passed directly
var doubled = processNumbers(x => x * 2, [1, 2, 3, 4])
var squared = processNumbers(x => x * x, [1, 2, 3, 4])

fmt.Printf("Doubled: %v\n", doubled) // Output: [2, 4, 6, 8]
fmt.Printf("Squared: %v\n", squared) // Output: [1, 4, 9, 16]

Type Safety and Error Detection

Harneet's function type system provides comprehensive compile-time validation:

Type Safety Examples
package main
import fmt

function apply(f function(int) int, value int) int {
    return f(value)
}

function double(x int) int {
    return x * 2
}

function toUpper(s string) string {
    return "[UPPER]" + s
}

// ✅ This works - correct types
var result1 = apply(double, 5)

// ❌ These would cause compile-time errors:
// apply(toUpper, 5)     // Error: toUpper expects string, not int
// apply(5, 10)          // Error: 5 is not a function
// apply(double, "text") // Error: double expects int, not string

Advanced Function Type Patterns

Factory Functions with Type Safety

Factory Function Validator
package main
import fmt

// Factory function returning typed function
function createValidator(minLength int) function(string) bool {
    function validate(input string) bool {
        // In practice, you'd check string length
        return input != ""
    }
    return validate
}

// Type-safe usage
var passwordValidator = createValidator(8)
var isValid = passwordValidator("mypassword")
fmt.Printf("Password valid: %t\n", isValid)

Callback Pattern with Type Validation

Callback Pattern
package main
import fmt

// Function with callback parameter
function processData(data int, callback function(int) int, errorHandler function(string)) int {
    if data < 0 {
        errorHandler("Invalid data: negative number")
        return 0
    }
    return callback(data)
}

// Callback functions
function processValue(x int) int {
    return x * 2
}

function handleError(msg string) {
    fmt.Printf("Error: %s\n", msg)
}

// Type-safe callback usage
var result = processData(10, processValue, handleError)
fmt.Printf("Processed result: %d\n", result)

Key Benefits

🔒 Type Safety

  • Compile-time validation of function signatures
  • Prevention of runtime type errors
  • Clear error messages for type mismatches

🚀 Performance

  • No runtime type checking overhead
  • Optimized function calls
  • Early error detection

📖 Code Clarity

  • Self-documenting function interfaces
  • Clear expectations for function parameters
  • Better IDE support and tooling

🔧 Flexibility

  • Works with all function types (named, anonymous, arrow)
  • Supports complex nested function signatures
  • Compatible with existing code patterns

The function type system in Harneet provides the perfect balance of type safety and flexibility, enabling robust functional programming while maintaining code clarity and performance.