139 lines
3.9 KiB
Markdown
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.
|
|
|
|
---
|