# Functions in Purp 🟣

A comprehensive guide to writing and using functions in the Purp programming language.

## Table of Contents

1. [Introduction](#introduction)
2. [Defining Functions](#defining-functions)
3. [Calling Functions](#calling-functions)
4. [Parameters](#parameters)
5. [Return Values](#return-values)
6. [Scope and Variables](#scope-and-variables)
7. [Recursion](#recursion)
8. [Built-in Functions](#built-in-functions)
9. [Best Practices](#best-practices)
10. [Common Patterns](#common-patterns)
11. [Examples](#examples)

---

## Introduction

Functions in Purp are reusable blocks of code that perform specific tasks. They help you:

- **Organize code** into logical, manageable pieces
- **Avoid repetition** by reusing the same code
- **Abstract complexity** by hiding implementation details
- **Test easily** by isolating functionality

Purp uses the `define` keyword to create functions, making them easy to read and write.

---

## Defining Functions

### Basic Syntax

```purp
define function_name()
    -- function body
end
```

### With Parameters

```purp
define function_name(param1, param2)
    -- function body using param1 and param2
end
```

### With Return Value

```purp
define function_name(param1, param2)
    result is param1 + param2
    return result
end
```

### Complete Example

```purp
-- A simple greeting function
define greet(name)
    say "Hello, " + name + "!"
end

-- Call the function
greet("World")      -- Output: Hello, World!
greet("Purp")       -- Output: Hello, Purp!
```

---

## Calling Functions

### Basic Call

```purp
-- Define the function
define say_hello()
    say "Hello!"
end

-- Call it
say_hello()
```

### With Arguments

```purp
define add(a, b)
    return a + b
end

-- Call with values
result is add(5, 3)
say result              -- Output: 8

-- Call with variables
x is 10
y is 20
sum is add(x, y)
say sum                 -- Output: 30

-- Call with expressions
total is add(2 + 3, 4 * 2)
say total               -- Output: 13
```

### Nested Calls

```purp
define double(n)
    return n * 2
end

define square(n)
    return n * n
end

-- Nest function calls
result is double(square(3))
say result              -- Output: 18 (3² = 9, 9 * 2 = 18)

-- Chain multiple calls
value is double(double(double(2)))
say value               -- Output: 16 (2→4→8→16)
```

### Using Return Values

```purp
define calculate_area(width, height)
    return width * height
end

-- Store the result
area is calculate_area(10, 5)
say "Area: " + text(area)    -- Output: Area: 50

-- Use directly in expressions
if calculate_area(4, 5) > 15
    say "Large area!"
end

-- Use in other function calls
say "The area is: " + text(calculate_area(3, 7))
```

---

## Parameters

### Required Parameters

All parameters in Purp are required. You must provide values for each one:

```purp
define greet(first_name, last_name)
    say "Hello, " + first_name + " " + last_name + "!"
end

greet("John", "Doe")    -- Works: Hello, John Doe!
-- greet("John")        -- Error: missing argument
```

### Multiple Parameters

Functions can have any number of parameters:

```purp
define describe_person(name, age, city, job)
    say name + " is " + text(age) + " years old"
    say "Lives in " + city
    say "Works as a " + job
end

describe_person("Alice", 28, "New York", "Developer")
```

### Parameter Types

Parameters can accept any Purp value type:

```purp
-- Numbers
define double(n)
    return n * 2
end

-- Strings
define shout(message)
    return message + "!"
end

-- Booleans
define toggle(flag)
    return not flag
end

-- Lists
define first_item(items)
    return items[0]
end

-- Maps
define get_name(person)
    return person.name
end

-- Mixed types
define format_user(name, age, is_active)
    status is "inactive"
    if is_active
        status is "active"
    end
    return name + " (" + text(age) + ") - " + status
end
```

### Working with Lists

```purp
define sum_list(numbers)
    total is 0
    for n in numbers
        total is total + n
    end
    return total
end

nums is [1, 2, 3, 4, 5]
say sum_list(nums)          -- Output: 15
say sum_list([10, 20, 30])  -- Output: 60
```

### Working with Maps

```purp
define get_full_name(person)
    return person.first + " " + person.last
end

user is {first: "Jane", last: "Smith"}
say get_full_name(user)     -- Output: Jane Smith
```

---

## Return Values

### Returning a Value

Use `return` to send a value back from a function:

```purp
define multiply(a, b)
    return a * b
end

result is multiply(6, 7)
say result                  -- Output: 42
```

### Early Return

You can return early from a function:

```purp
define divide(a, b)
    if b == 0
        say "Error: Cannot divide by zero!"
        return nothing
    end
    return a / b
end

say divide(10, 2)           -- Output: 5
say divide(10, 0)           -- Output: Error message, then "nothing"
```

### Returning Different Types

```purp
define get_status(code)
    if code == 200
        return "OK"
    end
    if code == 404
        return "Not Found"
    end
    if code == 500
        return "Server Error"
    end
    return "Unknown"
end

say get_status(200)         -- Output: OK
say get_status(404)         -- Output: Not Found
```

### Returning Complex Values

```purp
-- Return a list
define get_range(start, finish)
    result is []
    i is start
    while i <= finish
        push(result, i)
        i is i + 1
    end
    return result
end

numbers is get_range(1, 5)
say numbers                 -- Output: [1, 2, 3, 4, 5]

-- Return a map
define create_point(x, y)
    return {x: x, y: y}
end

point is create_point(10, 20)
say point.x                 -- Output: 10
say point.y                 -- Output: 20

-- Return a calculated map
define describe_number(n)
    return {
        value: n,
        doubled: n * 2,
        squared: n * n,
        is_even: n % 2 == 0
    }
end

info is describe_number(4)
say info.squared            -- Output: 16
say info.is_even            -- Output: true
```

### Functions Without Return

Functions without a `return` statement return `nothing`:

```purp
define log_message(msg)
    say "[LOG] " + msg
end

result is log_message("Hello")
say result                  -- Output: nothing
```

---

## Scope and Variables

### Local Variables

Variables defined inside a function are local to that function:

```purp
define calculate()
    x is 10           -- Local variable
    y is 20           -- Local variable
    return x + y
end

calculate()
-- say x              -- Error: x is not defined outside the function
```

### Accessing Outer Variables

Functions can read variables from outer scopes:

```purp
greeting is "Hello"

define say_greeting(name)
    say greeting + ", " + name + "!"
end

say_greeting("World")       -- Output: Hello, World!
```

### Modifying Variables

Functions create their own local scope:

```purp
count is 0

define increment()
    count is count + 1      -- Creates a new local 'count'
end

say count                   -- Output: 0
increment()
say count                   -- Output: 0 (outer count unchanged)
```

### Shadowing

Local variables can shadow outer variables:

```purp
name is "Global"

define test()
    name is "Local"         -- Shadows the outer 'name'
    say name                -- Output: Local
end

test()
say name                    -- Output: Global
```

---

## Recursion

### What is Recursion?

A function that calls itself is recursive. Recursion is useful for problems that can be broken down into smaller, similar problems.

### Basic Recursion

```purp
define countdown(n)
    if n <= 0
        say "Liftoff!"
        return nothing
    end
    say n
    countdown(n - 1)
end

countdown(5)
-- Output:
-- 5
-- 4
-- 3
-- 2
-- 1
-- Liftoff!
```

### Factorial

A classic recursive example:

```purp
define factorial(n)
    if n <= 1
        return 1
    end
    return n * factorial(n - 1)
end

say factorial(5)            -- Output: 120 (5! = 5×4×3×2×1)
say factorial(10)           -- Output: 3628800
```

### Fibonacci

```purp
define fibonacci(n)
    if n <= 1
        return n
    end
    return fibonacci(n - 1) + fibonacci(n - 2)
end

-- Print first 10 Fibonacci numbers
for i in range(0, 10)
    write(text(fibonacci(i)) + " ")
end
-- Output: 0 1 1 2 3 5 8 13 21 34
```

### Sum of List (Recursive)

```purp
define sum_recursive(items, index)
    if index >= length(items)
        return 0
    end
    return items[index] + sum_recursive(items, index + 1)
end

numbers is [1, 2, 3, 4, 5]
say sum_recursive(numbers, 0)   -- Output: 15
```

---

## Built-in Functions

Purp provides many built-in functions that you can use immediately.

### Input/Output

| Function | Description | Example |
|----------|-------------|---------|
| `say(value)` | Print with newline | `say "Hello"` |
| `write(value)` | Print without newline | `write "Enter: "` |
| `ask(prompt)` | Read user input | `name is ask("Name? ")` |

```purp
say "Welcome to Purp!"
write "Enter your name: "
name is ask("")
say "Hello, " + name + "!"
```

### Type Functions

| Function | Description | Example |
|----------|-------------|---------|
| `type(value)` | Get type as string | `type(42)` → `"number"` |
| `number(value)` | Convert to number | `number("42")` → `42` |
| `text(value)` | Convert to string | `text(42)` → `"42"` |

```purp
age_str is "25"
age is number(age_str)
say type(age)               -- Output: number

count is 100
say "Count: " + text(count) -- Output: Count: 100
```

### Collection Functions

| Function | Description | Example |
|----------|-------------|---------|
| `length(value)` | Get length | `length([1,2,3])` → `3` |
| `push(list, item)` | Add to list | `push(nums, 4)` |
| `pop(list)` | Remove last item | `last is pop(nums)` |
| `range(start, end)` | Create number list | `range(1, 5)` → `[1,2,3,4]` |

```purp
-- length
say length("Hello")         -- Output: 5
say length([1, 2, 3])       -- Output: 3

-- push and pop
stack is []
push(stack, "first")
push(stack, "second")
push(stack, "third")
say pop(stack)              -- Output: third
say stack                   -- Output: [first, second]

-- range
for i in range(1, 6)
    say i                   -- Output: 1, 2, 3, 4, 5
end
```

### Math Functions

| Function | Description | Example |
|----------|-------------|---------|
| `abs(n)` | Absolute value | `abs(-5)` → `5` |
| `round(n)` | Round to integer | `round(3.7)` → `4` |
| `floor(n)` | Round down | `floor(3.9)` → `3` |
| `sqrt(n)` | Square root | `sqrt(16)` → `4` |

```purp
say abs(-42)                -- Output: 42
say round(3.5)              -- Output: 4
say floor(3.9)              -- Output: 3
say sqrt(25)                -- Output: 5
```

---

## Best Practices

### 1. Use Descriptive Names

```purp
-- Bad
define f(x)
    return x * 2
end

-- Good
define double_value(number)
    return number * 2
end
```

### 2. Keep Functions Small

Each function should do one thing well:

```purp
-- Bad: Does too many things
define process_user(name, email, age)
    -- validate
    -- format
    -- save
    -- send email
    -- log
end

-- Good: Split into focused functions
define validate_user(name, email, age)
    -- validation logic
end

define save_user(user)
    -- save logic
end

define notify_user(email)
    -- notification logic
end
```

### 3. Use Early Returns

```purp
-- Less readable
define get_grade(score)
    result is ""
    if score >= 90
        result is "A"
    else
        if score >= 80
            result is "B"
        else
            if score >= 70
                result is "C"
            else
                result is "F"
            end
        end
    end
    return result
end

-- More readable with early returns
define get_grade(score)
    if score >= 90
        return "A"
    end
    if score >= 80
        return "B"
    end
    if score >= 70
        return "C"
    end
    return "F"
end
```

### 4. Document Complex Functions

```purp
-- Calculate the nth Fibonacci number
-- Uses recursion with memoization concept
-- Parameters:
--   n: The position in the sequence (0-indexed)
-- Returns:
--   The Fibonacci number at position n
define fibonacci(n)
    if n <= 1
        return n
    end
    return fibonacci(n - 1) + fibonacci(n - 2)
end
```

### 5. Validate Input

```purp
define divide(a, b)
    if b == 0
        say "Error: Division by zero"
        return nothing
    end
    return a / b
end

define get_item(items, index)
    if index < 0
        say "Error: Negative index"
        return nothing
    end
    if index >= length(items)
        say "Error: Index out of bounds"
        return nothing
    end
    return items[index]
end
```

---

## Common Patterns

### Pattern 1: Accumulator

Building up a result over iterations:

```purp
define sum(numbers)
    total is 0
    for n in numbers
        total is total + n
    end
    return total
end

define join_strings(strings, separator)
    result is ""
    first is true
    for s in strings
        if first
            result is s
            first is false
        else
            result is result + separator + s
        end
    end
    return result
end

words is ["Hello", "World", "Purp"]
say join_strings(words, " ")    -- Output: Hello World Purp
```

### Pattern 2: Filter

Selecting items that match a condition:

```purp
define get_evens(numbers)
    result is []
    for n in numbers
        if n % 2 == 0
            push(result, n)
        end
    end
    return result
end

nums is [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
evens is get_evens(nums)
say evens                       -- Output: [2, 4, 6, 8, 10]
```

### Pattern 3: Transform/Map

Applying an operation to each item:

```purp
define double_all(numbers)
    result is []
    for n in numbers
        push(result, n * 2)
    end
    return result
end

nums is [1, 2, 3, 4, 5]
doubled is double_all(nums)
say doubled                     -- Output: [2, 4, 6, 8, 10]
```

### Pattern 4: Find

Searching for an item:

```purp
define find_index(items, target)
    i is 0
    for item in items
        if item == target
            return i
        end
        i is i + 1
    end
    return -1
end

names is ["Alice", "Bob", "Charlie"]
say find_index(names, "Bob")    -- Output: 1
say find_index(names, "Dave")   -- Output: -1
```

### Pattern 5: Reduce

Combining all items into one value:

```purp
define max_value(numbers)
    if length(numbers) == 0
        return nothing
    end
    result is numbers[0]
    for n in numbers
        if n > result
            result is n
        end
    end
    return result
end

define min_value(numbers)
    if length(numbers) == 0
        return nothing
    end
    result is numbers[0]
    for n in numbers
        if n < result
            result is n
        end
    end
    return result
end

nums is [3, 1, 4, 1, 5, 9, 2, 6]
say max_value(nums)             -- Output: 9
say min_value(nums)             -- Output: 1
```

### Pattern 6: Factory

Creating and returning objects:

```purp
define create_user(name, age)
    return {
        name: name,
        age: age,
        is_adult: age >= 18
    }
end

user1 is create_user("Alice", 25)
user2 is create_user("Bob", 16)

say user1.name + " is adult: " + text(user1.is_adult)
say user2.name + " is adult: " + text(user2.is_adult)
```

---

## Examples

### Example 1: String Utilities

```purp
-- Repeat a string n times
define repeat_string(str, times)
    result is ""
    for i in range(0, times)
        result is result + str
    end
    return result
end

say repeat_string("Ha", 3)      -- Output: HaHaHa
say repeat_string("-", 10)      -- Output: ----------

-- Count occurrences of a character
define count_char(str, char)
    count is 0
    i is 0
    while i < length(str)
        -- Note: Purp doesn't have char indexing yet
        -- This is a conceptual example
        i is i + 1
    end
    return count
end
```

### Example 2: Math Utilities

```purp
-- Calculate power (base^exponent)
define power(base, exponent)
    if exponent == 0
        return 1
    end
    result is 1
    for i in range(0, exponent)
        result is result * base
    end
    return result
end

say power(2, 10)                -- Output: 1024
say power(5, 3)                 -- Output: 125

-- Check if a number is prime
define is_prime(n)
    if n < 2
        return false
    end
    if n == 2
        return true
    end
    if n % 2 == 0
        return false
    end
    i is 3
    while i * i <= n
        if n % i == 0
            return false
        end
        i is i + 2
    end
    return true
end

say is_prime(17)                -- Output: true
say is_prime(18)                -- Output: false

-- Get all primes up to n
define primes_up_to(n)
    result is []
    for i in range(2, n + 1)
        if is_prime(i)
            push(result, i)
        end
    end
    return result
end

say primes_up_to(30)
-- Output: [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
```

### Example 3: List Operations

```purp
-- Reverse a list
define reverse_list(items)
    result is []
    i is length(items) - 1
    while i >= 0
        push(result, items[i])
        i is i - 1
    end
    return result
end

say reverse_list([1, 2, 3, 4, 5])   -- Output: [5, 4, 3, 2, 1]

-- Check if list contains value
define contains(items, value)
    for item in items
        if item == value
            return true
        end
    end
    return false
end

say contains([1, 2, 3], 2)      -- Output: true
say contains([1, 2, 3], 5)      -- Output: false

-- Remove duplicates
define unique(items)
    result is []
    for item in items
        if not contains(result, item)
            push(result, item)
        end
    end
    return result
end

say unique([1, 2, 2, 3, 3, 3, 4])   -- Output: [1, 2, 3, 4]
```

### Example 4: Complete Program - Grade Calculator

```purp
-- Grade Calculator Program

define calculate_average(grades)
    if length(grades) == 0
        return 0
    end
    total is 0
    for grade in grades
        total is total + grade
    end
    return total / length(grades)
end

define get_letter_grade(score)
    if score >= 90
        return "A"
    end
    if score >= 80
        return "B"
    end
    if score >= 70
        return "C"
    end
    if score >= 60
        return "D"
    end
    return "F"
end

define get_gpa(letter)
    if letter == "A"
        return 4.0
    end
    if letter == "B"
        return 3.0
    end
    if letter == "C"
        return 2.0
    end
    if letter == "D"
        return 1.0
    end
    return 0.0
end

define print_report(name, grades)
    say "=== Grade Report for " + name + " ==="
    say ""
    
    say "Individual Grades:"
    for i in range(0, length(grades))
        grade is grades[i]
        letter is get_letter_grade(grade)
        say "  Test " + text(i + 1) + ": " + text(grade) + " (" + letter + ")"
    end
    
    say ""
    average is calculate_average(grades)
    final_letter is get_letter_grade(average)
    gpa is get_gpa(final_letter)
    
    say "Average: " + text(round(average))
    say "Final Grade: " + final_letter
    say "GPA: " + text(gpa)
end

-- Run the program
student_grades is [85, 92, 78, 95, 88]
print_report("Alice", student_grades)
```

**Output:**
```
=== Grade Report for Alice ===

Individual Grades:
  Test 1: 85 (B)
  Test 2: 92 (A)
  Test 3: 78 (C)
  Test 4: 95 (A)
  Test 5: 88 (B)

Average: 88
Final Grade: B
GPA: 3.0
```

---

## Summary

Functions in Purp are powerful tools for organizing and reusing code. Remember:

1. Use `define` to create functions
2. Use `return` to send values back
3. Parameters are passed by value
4. Functions create their own local scope
5. Recursion is supported for complex algorithms
6. Built-in functions provide common operations

Happy coding with Purp! 🟣
