Functions
Functions are the building blocks of Twinkies programs. They allow you to organize code into reusable, named units.
Function Declaration
Functions are declared using the func keyword:
func function_name(parameters) -> return_type {
// Function body
return value;
}
Basic Example
func greet() -> void {
print("Hello, World!");
}
func add(x: int, y: int) -> int {
return x + y;
}
Function Parameters
Functions can have zero or more parameters, each with a type:
func no_params() -> int {
return 42;
}
func one_param(x: int) -> int {
return x * 2;
}
func multiple_params(a: int, b: int, c: int) -> int {
return a + b + c;
}
Parameter Types
Parameters can be any type:
func process_string(s: string) -> int {
return strlen(s);
}
func check_flag(flag: bool) -> bool {
return !flag;
}
func sum_array(arr: int[5]) -> int {
let total: int = 0;
let i: int = 0;
while (i < 5) {
total = total + arr[i];
i = i + 1;
}
return total;
}
Return Types
Functions must specify a return type:
func returns_int() -> int {
return 42;
}
func returns_bool() -> bool {
return true;
}
func returns_string() -> string {
return "Hello";
}
func returns_nothing() -> void {
print("No return value");
}
Return Statements
Use return to exit a function and optionally return a value:
func early_return(x: int) -> int {
if (x < 0) {
return 0; // Early exit
}
return x * 2;
}
The Main Function
Every program must have a main() function as the entry point:
func main() -> int {
// Program starts here
return 0; // 0 typically means success
}
Function Calls
Call functions using parentheses:
func add(x: int, y: int) -> int {
return x + y;
}
func main() -> int {
let result: int = add(10, 20); // Call function
print(result); // Prints 30
return 0;
}
Function Call Arguments
Arguments must match parameter types:
func multiply(x: int, y: int) -> int {
return x * y;
}
func main() -> int {
let product: int = multiply(5, 6); // ✓ Correct
// let bad: int = multiply(5); // ✗ Error: wrong number of arguments
return 0;
}
Function Overloading
Twinkies supports function overloading - multiple functions with the same name but different parameter types:
// Overload 1: one int parameter
func add(x: int) -> int {
return x + 1;
}
// Overload 2: two int parameters
func add(x: int, y: int) -> int {
return x + y;
}
// Overload 3: bool parameter
func add(flag: bool) -> int {
if (flag) {
return 42;
}
return 0;
}
func main() -> int {
let a: int = add(5); // Calls overload 1
let b: int = add(2, 3); // Calls overload 2
let c: int = add(true); // Calls overload 3
return 0;
}
Overload Resolution
The compiler selects the best matching function:
- Exact match - Parameter types match exactly
- Implicit conversion - Arguments can be converted to parameter types
- Fewest conversions - If multiple overloads match, the one requiring fewest conversions is chosen
- Ambiguity error - If multiple overloads require the same number of conversions, an error is reported
func process(x: int) -> int {
return x;
}
func process(x: float) -> int {
return x;
}
func main() -> int {
process(10); // ✓ Calls int version (exact match)
process(3.14); // ✓ Calls float version (exact match)
// process(true); // ✗ Error: ambiguous (could convert to int or float)
return 0;
}
Recursion
Functions can call themselves recursively:
func factorial(n: int) -> int {
if (n <= 1) {
return 1;
}
return n * factorial(n - 1); // Recursive call
}
func fibonacci(n: int) -> int {
if (n <= 1) {
return n;
}
return fibonacci(n - 1) + fibonacci(n - 2); // Recursive calls
}
Nested Function Calls
Functions can be called within other function calls:
func add(x: int, y: int) -> int {
return x + y;
}
func multiply(x: int, y: int) -> int {
return x * y;
}
func main() -> int {
let result: int = multiply(add(2, 3), add(4, 5)); // Nested calls
// result = multiply(5, 9) = 45
return 0;
}
Function Scope
Functions create their own scope. Variables declared inside a function are local to that function:
func example() -> int {
let local: int = 10; // Local variable
return local;
}
func main() -> int {
// print(local); // ✗ Error: local is not in scope
let result: int = example();
return 0;
}
Parameter Passing
Parameters are passed by value (copied):
func modify(x: int) -> int {
x = 100; // Only modifies local copy
}
func main() -> int {
let value: int = 10;
modify(value);
print(value); // Still prints 10 (not 100)
return 0;
}
Function Examples
Simple Calculator
func add(x: int, y: int) -> int {
return x + y;
}
func subtract(x: int, y: int) -> int {
return x - y;
}
func multiply(x: int, y: int) -> int {
return x * y;
}
func divide(x: int, y: int) -> int {
if (y == 0) {
return 0; // Handle division by zero
}
return x / y;
}
Greatest Common Divisor (GCD)
func gcd(a: int, b: int) -> int {
if (b == 0) {
return a;
}
return gcd(b, a % b); // Euclidean algorithm
}
Prime Number Checker
func is_prime(n: int) -> int {
if (n < 2) {
return false;
}
let i: int = 2;
while (i * i <= n) {
if (n % i == 0) {
return false;
}
i = i + 1;
}
return true;
}
Best Practices
- Use descriptive names - Function names should clearly describe what they do
- Keep functions focused - Each function should do one thing well
- Use appropriate return types - Use
voidwhen no return value is needed - Handle edge cases - Check for division by zero, null values, etc.
- Document complex functions - Use comments to explain complex logic
Next Steps
- Learn about Control Flow for conditional logic in functions
- Read Arrays and Strings for functions that work with arrays
- Check Modules for organizing functions into modules
