Associated Functions as a Language Feature
Status: Approved Approved: 2026-01-31 Author: Claude Created: 2026-01-31 Supersedes: associated-functions-proposal.md Depends On: None
Summary
Implement associated functions as a general language feature that works for any type with an impl block, rather than hardcoding support for specific types like Duration and Size.
Motivation
The current implementation of associated functions is a hack:
fn is_type_name_for_associated_functions(name: &str) -> bool {
matches!(name, "Duration" | "Size")
}
This hardcodes specific type names in the compiler. Associated functions should work for ANY user-defined or built-in type that has methods defined without self in an impl block.
Use Cases
-
User-defined constructors:
type Point = { x: int, y: int } impl Point { @origin () -> Self = Point { x: 0, y: 0 } @new (x: int, y: int) -> Self = Point { x, y } } let p = Point.origin() let q = Point.new(x: 10, y: 20) -
Builder pattern:
type Config = { host: str, port: int, timeout: Duration } impl Config { @default () -> Self = Config { host: "localhost", port: 8080, timeout: 30s } @with_host (self, host: str) -> Self = Config { ...self, host } @with_port (self, port: int) -> Self = Config { ...self, port } @with_timeout (self, timeout: Duration) -> Self = Config { ...self, timeout } } let cfg = Config.default().with_host(host: "example.com").with_port(port: 443) -
Factory methods on generic types:
impl<T> Option<T> { @some (value: T) -> Self = Some(value) @none () -> Self = None } let x = Option<int>.some(value: 42) -
Namespace organization:
type Math = {} impl Math { @sqrt (x: float) -> float = ... @abs (x: float) -> float = ... } let r = Math.sqrt(x: 2.0)
Design
Definition
An associated function is a function defined in an impl block without a self parameter:
impl MyType {
// Instance method (has self)
@get_value (self) -> int = self.value
// Associated function (no self)
@create (value: int) -> Self = MyType { value }
}
Calling Syntax
Associated functions are called using Type.method(args):
let instance = MyType.create(value: 42)
Type Resolution
When the type checker encounters Ident.method(...):
- Look up
Identin the current scope - If it resolves to a value → treat as instance method call on that value
- If it resolves to a type name → look up associated function in that type’s impl blocks
- If no associated function found → error
Impl Block Processing
When processing impl blocks, distinguish between:
- Methods with
selfparameter → register as instance methods - Methods without
selfparameter → register as associated functions
Chaining
Associated functions that return Self enable method chaining:
impl Builder {
@new () -> Self = Builder { ... }
@with_name (self, name: str) -> Self = Builder { ...self, name }
@with_value (self, value: int) -> Self = Builder { ...self, value }
@build (self) -> Product = ...
}
let product = Builder.new()
.with_name(name: "example")
.with_value(value: 42)
.build()
Generic Types
For generic types, type arguments must be provided:
impl<T> Option<T> {
@some (value: T) -> Self = Some(value)
}
let x = Option<int>.some(value: 42)
Visibility
Associated functions follow the same visibility rules as instance methods:
impl Point {
pub @new (x: int, y: int) -> Point = Point { x, y } // Public
@internal () -> Point = Point { x: 0, y: 0 } // Private
}
Error Cases
It is an error if:
- A type name is used in expression position except as the receiver of a method call
- An associated function is called on a type that has no such function defined
- A local binding shadows a type name when the intent is to call an associated function
let Duration = 10
let d = Duration.from_seconds(s: 5) // Error: Duration is a value (int), not a type
// Rename variable to access type's associated functions
Migration
- Remove
is_type_name_for_associated_functions()checks in type checker and evaluator - Implement general associated function lookup via impl blocks
- Register Duration/Size factory methods as impl-block associated functions
Testing
Tests should cover:
- Basic associated functions on user-defined types
- Method chaining with builder pattern (
Type.new().configure().build()) - Generic types with type parameters
- Self return type usage
- Visibility (pub vs private associated functions)
- Shadowing (local variable shadows type name)
- Error cases (calling non-existent associated function)
Alternatives Considered
Keep Hardcoded Type Names
Continue adding types to is_type_name_for_associated_functions() as needed.
Rejected: Doesn’t scale, prevents user-defined associated functions.
Separate Syntax for Associated Functions
Use Type::method() (Rust-style) instead of Type.method().
Rejected: Inconsistent with Ori’s uniform dot syntax for member access.
References
- Current implementation:
compiler/ori_typeck/src/infer/call.rs - Spec:
docs/ori_lang/v2026/spec/08-declarations.md§ Associated Functions