Skip to content

RFC - Generic trait-targeted methods #581

@dannymeijer

Description

@dannymeijer

Use this issue to propose an RFC topic and get early alignment.
If accepted, we’ll typically ask for a PR adding an RFC document under docs/RFCs/ based on the RFC template.

RFCs are user-facing only (language features, documentation, CLI/tooling, runtime surface area).
If your proposal is about Rust internals/compiler architecture, please open a bug/chore/feature issue instead.

Area

  • Incan Language (syntax/semantics)
  • Runtime / Core crates (stdlib/core/derive)
  • Documentation

Summary

RFC 099 proposes generic trait-targeted methods: methods declared inside the owning type body may carry method-level type parameters, target a trait instantiation with for Trait[...], and add where constraints that define a checked family of trait adoptions rather than one concrete adoption at a time. The motivating case is numeric and storage-carrier dispatch for APIs such as std.io.BytesIO.write(...), while keeping the authoring model class-body and Python-shaped rather than Rust-shaped impl Trait for Type blocks.

Motivation

RFC 009 made exact-width numeric types first-class, and RFC 056 made binary numeric I/O trait-backed rather than a matrix of width-specific method names. RFC 091 adds constrained integer newtype storage carriers, which makes representation-oriented work want to delegate through a storage carrier without pretending the domain newtype is its carrier type. The missing language surface is a way for an owning class to define one generic method family that satisfies a generic trait target under explicit constraints.

Proposal sketch

Allow class-body methods to put method generics, the trait target, and where constraints together in the method generic header:

class BytesIO:
    def write[T with FixedWidthNumeric for BinaryWrite[T]](
        mut self,
        value: T,
        endian: Endian,
    ) -> Result[None, IoError]:
        ...

Storage-backed newtypes can delegate through an explicit storage projection without implicit conversion:

type Month = newtype int[ge=1, le=12, storage=u8]

class BytesIO:
    def write[
        T with StorageBackedInteger
        for BinaryWrite[T]
        where T.Storage with FixedWidthInteger
        where BytesIO with BinaryWrite[T.Storage]
    ](
        mut self,
        value: T,
        endian: Endian,
    ) -> Result[None, IoError]:
        storage: T.Storage = value.to_storage()
        return self.write(storage, endian)

The method lives on the owning class. extends remains class inheritance, and source does not gain Rust-style impl Trait for Type syntax.

Draft RFC file: workspaces/docs-site/docs/RFCs/099_generic_trait_targeted_methods.md.

Alternatives considered

Use external Rust-shaped impl Trait for Type blocks. That maps cleanly to Rust but violates the Incan direction from RFC 043, where users adopt capabilities with with and write behavior in type bodies.

Use external extend Type with Trait blocks. That is more Incan-looking but overloads extend/extends, which should stay class inheritance and behavior reuse terminology.

Repeat concrete adoptions for every numeric width. That avoids new syntax but recreates boilerplate and leaves storage-backed newtypes without a principled representation-delegation path.

Let storage-backed newtypes behave as their storage carrier. That is unsafe for the RFC 091 model: storage is representation metadata, not the source-level type.

Add general method overloading. That is broader than needed; the requested feature is checked trait conformance over a generic family, not runtime-style overload selection by shape.

Impact / compatibility

This is additive. Existing methods and trait adoptions continue to work. The new syntax increases method-header expressiveness and requires overlap diagnostics so it does not accidentally add specialization. int remains the RFC 009 alias for i64, and storage-backed newtypes remain nominal domain types rather than subtypes or implicit conversions to their storage carriers.

Implementation notes (optional)

Likely layers: parser/AST for for TraitExpression and where inside method generic headers, typechecker/symbol resolution for adoption-family metadata and overlap rejection, associated type projection support from RFC 098 for T.Storage, stdlib numeric-family traits, formatter support for multiline headers, LSP diagnostics/hover/completion, and Rust backend emission that can realize the checked adoption family without exposing Rust impl syntax in source.

Checklist

  • I checked for an existing RFC/issue covering this.
  • I can describe how this impacts existing code and how to migrate (if needed).

Metadata

Metadata

Assignees

No one assigned

    Labels

    RFCAdding or updating RFC documentsdocumentationImprovements or additions to documentationincan language semanticsSuggestions, features, or bugs related to the Incan Language itself (syntax and semantics)runtime / core cratesSuggestions, features, or bugs related to the `incan-core`, `incan-stdlib`, 'incan-derive` crates

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions