YREA-SLS/docs/functions.md

139 lines
3.9 KiB
Markdown

---
Title: 5 Functions
Prev: Basic Operations
Next: Control Flow
---
## 5. Functions
Functions are user-defined procedures that encapsulate reusable code. They are the primary abstraction mechanism in the language.
### 5.1 What are Functions
Functions differ from operators:
- **Operators** implement trait methods and are the fundamental building blocks
- **Functions** are user-defined procedures that use operators
- Operators and functions cannot share names
> **Related**: See Section 1 "Operators vs Functions" for a complete explanation of the distinction.
### 5.2 Defining Functions
**Syntax**: `(inputs -- outputs) { body } ::name fn`
The function signature specifies stack effects (what is consumed and produced), the body contains the implementation, and the name identifies the function.
**Examples**:
```
// Simple function - requires Multiplyable trait
(Multiplyable -- Multiplyable) { dup * } ::square fn
// Use it
5 square // => 25
// Multiple inputs and outputs
(Number Number -- Number Number) {
over over / swap %
} ::divmod fn
10 3 divmod // => 3 1 (quotient remainder)
```
**Function Bodies**: The `{ }` braces contain a TokenString that is parsed as the function body when the function is defined. See Section 5.5 for details on TokenStrings.
> **Related**: See Section 10.2 for generic functions with type parameters.
### 5.3 Calling Functions
Functions are called by simply writing their name. The postfix notation means arguments must be on the stack before the function name:
```
// Define a function
(Number Number -- Number) { + } ::add fn
// Call it
3 4 add // => 7
// Chain functions
5 square 2 * // => 50 (square 5, then multiply by 2)
```
**Function Chaining**: Since everything is postfix, functions naturally chain left-to-right:
```
10 square 2 / 5 + // ((10²) / 2) + 5 = 55
```
### 5.4 Recursion
Functions can call themselves recursively:
```
(Number -- Number) {
dup 1 <=
{ drop 1 }
{ dup 1 - factorial * }
if
} ::factorial fn
5 factorial // => 120
```
**Stack Considerations**: Recursive functions consume stack space. Deep recursion may cause stack overflow. Consider iterative alternatives for performance-critical code.
### 5.5 Token Strings
Token strings are lexed but unparsed code blocks enclosed in `{ }`. They are parsed differently depending on which operator uses them.
**Operators that parse TokenStrings:**
- `fn` - Parses as function body (code block)
- `trait` - Parses as trait definition (method signatures)
- `impl` - Parses as trait implementation (method definitions)
- `eval` - Parses and executes as code block immediately
- `lambda` - Parses as code block, pushes the block as a callable value
- `if` - Parses both TokenStrings as code blocks (then/else branches)
- `while` - Parses both TokenStrings as code blocks (condition/body)
- `for` - Parses TokenString as code block (loop body)
- `match` - Parses TokenString as pattern matching arms
- `map`, `filter`, `reduce`, `each` - Parse TokenStrings as code blocks for array operations
**Examples**:
```
// Function body (parsed by fn)
(Number -- Number) { dup * } ::square fn
// Conditional branches (parsed by if)
x 0 > { "positive" print } { "negative" print } if
// Loop body (parsed by while)
{ dup 10 < } { dup print 1 + } while
```
### 5.6 Lambda Functions
The `lambda` operator converts a TokenString into a callable code block that can be stored and passed around:
```
{ dup * } lambda // Creates a callable code block
::square_fn swap // Store reference to the lambda
// Later use:
5 square_fn eval // Calls the lambda: pushes 25
```
**Use Cases**:
- Creating first-class functions
- Passing code blocks as values
- Dynamic dispatch
- Higher-order operations
**Example with arrays**:
```
// Store a lambda and use it with map
{ 2 * } lambda ::double swap
[1 2 3 4] double map // => [2 4 6 8]
```
> **Related**: See Section 11.1 for the `eval` operator used to execute lambdas.
---