174 lines
3.9 KiB
Markdown
174 lines
3.9 KiB
Markdown
|
|
## 10. Generic Programming
|
|
|
|
### 10.1 Type Parameters
|
|
|
|
Type parameters allow functions, structs, and traits to work with multiple types.
|
|
|
|
**Generic Syntax**: `<T>` where T is a type parameter
|
|
|
|
**Examples**:
|
|
```
|
|
// Generic struct
|
|
(T T --) { x: y: } ::Point<T> struct
|
|
|
|
// Generic union
|
|
(T --) { Some(T) None } ::Option<T> union
|
|
|
|
// Generic trait
|
|
{
|
|
(Self T -- Self) append:
|
|
} ::Container<T> trait
|
|
```
|
|
|
|
**Type Parameter Constraints**:
|
|
|
|
When operations are performed on type parameters, they must be constrained by traits:
|
|
|
|
```
|
|
// Unconstrained - no operations on T
|
|
(T -- T) { } ::identity fn
|
|
|
|
// Constrained - T must support multiplication
|
|
(T:Multiplyable -- T) { dup * } ::square fn
|
|
```
|
|
|
|
**Multiple Type Parameters**:
|
|
```
|
|
(T E --) { Ok(T) Err(E) } ::Result<T E> union
|
|
```
|
|
|
|
### 10.2 Generic Functions
|
|
|
|
Generic functions work with multiple types by using trait constraints in their type tuples.
|
|
|
|
**Syntax**: `(trait_constraints -- outputs) { body } ::name fn`
|
|
|
|
**Examples**:
|
|
```
|
|
// Generic identity - works with any type (no operations, no constraint needed)
|
|
(T -- T) { } ::identity fn
|
|
|
|
// Requires Addable trait
|
|
(Addable Addable -- Addable) {
|
|
+
|
|
} ::add_values fn
|
|
|
|
// Requires Number trait
|
|
(Number -- Number) {
|
|
dup 0 > { } { 0 swap - } if
|
|
} ::abs fn
|
|
|
|
// Multiple constraints
|
|
(Number Number -- Number) {
|
|
dup * swap dup * + // a² + b²
|
|
} ::pythagorean fn
|
|
```
|
|
|
|
**Type Inference**: The compiler infers the actual type from usage:
|
|
```
|
|
5 identity // T inferred as i64
|
|
"hello" identity // T inferred as String
|
|
3 4 add_values // Addable inferred as i64
|
|
```
|
|
|
|
### 10.3 Generic Data Structures
|
|
|
|
Structs and unions can be generic over type parameters:
|
|
|
|
**Generic Structs**:
|
|
```
|
|
// Point is generic over coordinate type
|
|
(T T --) { x: y: } ::Point<T> struct
|
|
|
|
// Use with different types
|
|
3.0 4.0 Point // Point<f64>
|
|
3 4 Point // Point<i64>
|
|
|
|
// Multiple type parameters
|
|
(T U --) { first: second: } ::Pair<T U> struct
|
|
5 "hello" Pair // Pair<i64 String>
|
|
```
|
|
|
|
**Generic Unions**:
|
|
```
|
|
// Option is generic over contained type
|
|
(T --) { Some(T) None } ::Option<T> union
|
|
|
|
42 Option::Some // Option<i64>::Some
|
|
"text" Option::Some // Option<String>::Some
|
|
Option::None // Option<T>::None (T inferred from context)
|
|
|
|
// Result with two type parameters
|
|
(T E --) { Ok(T) Err(E) } ::Result<T E> union
|
|
```
|
|
|
|
**Nested Generics**:
|
|
```
|
|
// Array of Options
|
|
[Option::Some Option::None] // Array of Option<T>
|
|
|
|
// Option of array
|
|
[1 2 3] Option::Some // Option<Array<i64>>
|
|
```
|
|
|
|
### 10.4 Generic Traits
|
|
|
|
Traits can be generic, allowing them to describe behavior parameterized over types.
|
|
|
|
**Generic Trait Syntax**:
|
|
```
|
|
// Container generic over element type
|
|
{
|
|
(Self T -- Self) append:
|
|
(Self -- T) pop:
|
|
} ::Container<T> trait
|
|
|
|
// Map generic over key and value types
|
|
{
|
|
(Self K -- V) get:
|
|
(Self K V -- Self) insert:
|
|
} ::Map<K V> trait
|
|
```
|
|
|
|
**Generic Trait Inheritance**:
|
|
|
|
When inheriting from generic traits, you must either:
|
|
1. Make the inheriting trait similarly generic
|
|
2. Specify concrete types for the generic parameters
|
|
|
|
```
|
|
// Inheriting trait is also generic
|
|
[ ::Container<T> ] ::Stack<T> inher
|
|
{
|
|
(Self -- T) peek:
|
|
} ::Stack<T> trait
|
|
|
|
// Inheriting trait specifies concrete type
|
|
[ ::Container<i32> ] ::IntStack inher
|
|
{
|
|
(Self -- i32) peek:
|
|
} ::IntStack trait
|
|
|
|
// Multiple generic inheritance
|
|
[ ::Selectable<T> ::Sized ::Sliceable ] ::ArrayOf<T> inher
|
|
{ } ::ArrayOf<T> trait
|
|
```
|
|
|
|
### 10.5 Type Parameter Enforcement
|
|
|
|
**Current Behavior**: Type parameters are currently suggestions when parsing code blocks. The compiler does not yet enforce that type parameters actually constrain how operators and functions act at parse time.
|
|
|
|
**Example**:
|
|
```
|
|
// Currently no error even without Multiplyable constraint
|
|
(T -- T) { dup * } ::square fn
|
|
|
|
// Should require constraint
|
|
(Multiplyable -- Multiplyable) { dup * } ::square fn
|
|
```
|
|
|
|
> **Future Enhancement**: See Appendix F for planned type parameter enforcement at parse time.
|
|
|
|
---
|