Associated Functions Proposal
Status: Superseded Author: Claude Created: 2026-01-31 Approved: 2026-01-31 Superseded By: associated-functions-language-feature.md (2026-01-31)
Summary
Add support for associated functions (static methods) on types, enabling syntax like Duration.from_seconds(s: 10) and Size.from_bytes(b: 1024).
Motivation
The spec defines factory methods for Duration and Size types:
impl Duration {
@from_nanoseconds (ns: int) -> Duration
@from_seconds (s: int) -> Duration
// ...
}
These methods don’t have self as a parameter - they’re associated functions, not instance methods. Currently, method calls require a receiver value (value.method()), but associated functions are called on the type itself (Type.method()).
Use Cases
-
Factory methods: Create values with explicit semantics
Duration.from_seconds(s: 10)instead of10sSize.from_megabytes(mb: 100)instead of100mb
-
Constructor alternatives: Named constructors for complex types
Point.origin()instead ofPoint { x: 0, y: 0 }Result.ok(value:)andResult.err(error:)
-
Namespace organization: Group related functions with their type
Math.sqrt(x:)instead ofsqrt(x:)Random.int(min:, max:)instead of standalone functions
Design
Syntax
Associated functions are defined in impl blocks without a self parameter:
impl Duration {
// Instance method (has self)
@seconds (self) -> int = ...
// Associated function (no self)
@from_seconds (s: int) -> Duration = ...
}
Calling Syntax
Associated functions are called using Type.method(args):
let d = Duration.from_seconds(s: 10)
let s = Size.from_megabytes(mb: 100)
Type Resolution
When parsing Ident.method(...):
- If
Identresolves to a value → instance method call - If
Identresolves to a type name → associated function call - Otherwise → error
Self in Associated Functions
Associated functions may use Self as a return type, referring to the implementing type:
impl Point {
@origin () -> Self = Point { x: 0, y: 0 }
@new (x: int, y: int) -> Self = Point { x, y }
}
Self refers to the type being implemented, including any type parameters. It is only valid in the return type position, not as a parameter type (for associated functions).
Generic Types
For generic types, full type arguments are required:
let x: Option<int> = Option<int>.some(value: 42)
let r: Result<str, Error> = Result<str, Error>.ok(value: "success")
Type inference does not apply to the type name prefix in associated function calls.
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 to module
}
In a pub impl block, all associated functions are public.
Built-in Types
For primitive types (int, float, Duration, Size, etc.), associated functions are registered as built-in methods that don’t require self.
Trait Associated Functions
Traits may define associated functions that implementors must provide:
trait Default {
@default () -> Self
}
impl Point: Default {
@default () -> Self = Point { x: 0, y: 0 }
}
Calling associated functions via trait objects is not allowed (requires concrete type):
@make_default (d: Default) -> Default = d.default() // ERROR: cannot call associated function on trait object
This is consistent with object safety rules — associated functions returning Self prevent trait object usage.
Design Decisions
Extensions Cannot Have Associated Functions
Extensions cannot define associated functions (methods without self). Use inherent impl blocks for associated functions.
// ERROR: extensions cannot have associated functions
extend Point {
@origin () -> Point = Point { x: 0, y: 0 } // No self parameter
}
// OK: use impl block
impl Point {
@origin () -> Point = Point { x: 0, y: 0 }
}
Dot Syntax, Not Double Colon
Associated functions use . syntax for consistency with method calls:
Duration.from_seconds(s: 10) // Ori style
// Not: Duration::from_seconds(s: 10) // Rust style
This maintains uniform syntax for all member access.
Implementation
Phase 1: Type Names as Values
Make type names usable in expression position when followed by .:
Duration.from_seconds(...)- type followed by method access- Parser recognizes
TypeName.method(...)pattern
Phase 2: Associated Function Registry
Extend the method registry to support methods without self:
BuiltinMethodRegistrygainscheck_associated(type, method, args)- User
implblocks can define methods withoutself
Phase 3: Built-in Factory Methods
Implement Duration/Size factory methods:
Duration.from_nanoseconds(ns:),.from_microseconds(us:), etc.Size.from_bytes(b:),.from_kilobytes(kb:), etc.
Alternatives Considered
Module-Level Functions
Instead of Duration.from_seconds(s: 10), use duration_from_seconds(s: 10):
let d = duration_from_seconds(s: 10)
Rejected: Less discoverable, pollutes namespace, doesn’t match spec.
Constructor Syntax
Use the type as a function: Duration(seconds: 10):
let d = Duration(seconds: 10)
Rejected: Conflicts with newtype construction syntax, less explicit about which unit.
References
- Spec:
docs/ori_lang/v2026/spec/06-types.md§ Duration, Size - Roadmap:
plans/roadmap/phase-01-type-system.md§ 1.1A Duration/Size factory methods