Skip to content

feature - support generic function references for decorator factories #640

@dannymeijer

Description

@dannymeijer

Area

  • Incan Language (syntax/semantics)
  • Compiler (frontend/backend/codegen)

Problem statement

Generic decorator factories are not currently usable as ordinary decorator values. This forces library authors to define one decorator factory per concrete function signature when the intended behavior is signature-preserving identity metadata.

This appears to be a deferred feature rather than an implementation bug:

  • RFC 035 explicitly deferred generic function references in value position.
  • RFC 036 allows generic decorators only where the current type system can express the callable signature, and leaves more advanced callable polymorphism outside the RFC.

The practical effect shows up in InQL's function registry work. The desired library shape is one logical decorator factory that preserves whatever function signature it receives:

pub def registered[F](function_ref: str) -> ((F) -> F):
    def decorator(func: F) -> F:
        return func
    return decorator

@registered("inql.functions.col")
pub def col(name: str) -> ColumnExpr:
    ...

@registered("inql.functions.add")
pub def add(left: ColumnExpr, right: ColumnExpr) -> ColumnExpr:
    ...

Today the package has to spell signature-specific factories instead:

pub def register_str_to_column_expr(function_ref: str) -> (((str) -> ColumnExpr) -> ((str) -> ColumnExpr)):
    ...

pub def register_binary_column_expr(function_ref: str) -> (((ColumnExpr, ColumnExpr) -> ColumnExpr) -> ((ColumnExpr, ColumnExpr) -> ColumnExpr)):
    ...

That works, but it is poor library-author DX and creates avoidable registry boilerplate.

Proposed solution

Add support for generic function references in value/decorator position when the generic function can be explicitly instantiated or inferred from the decorator application context.

At minimum, support signature-preserving generic decorator factories such as:

pub def registered[F](function_ref: str) -> ((F) -> F):
    def decorator(func: F) -> F:
        return func
    return decorator

@registered[(str) -> ColumnExpr]("inql.functions.col")
pub def col(name: str) -> ColumnExpr:
    ...

Preferred end-state, if inference is tractable, is to allow the type argument to be inferred from the decorated function:

@registered("inql.functions.col")
pub def col(name: str) -> ColumnExpr:
    ...

The feature should compose with RFC 036 decorator desugaring: @registered(ref) should type-check as registered(ref)(decorated_function) after generic instantiation/inference.

Alternatives considered

  • Keep signature-specific decorator factories. This is compilable today, but it pushes mechanical boilerplate into libraries and makes registry-style metadata APIs noisier than they need to be.
  • Require closures at every use site. This avoids generic function references but does not work well for decorator syntax, where the decorated function binding is created by the declaration.
  • Wait for parameter packs or higher-rank callable polymorphism. That may still be needed for fully general decorators, but identity-style metadata decorators only need a generic function type parameter that can preserve one concrete signature.

Scope / acceptance criteria

  • In scope:

    • Generic function references in value position where type arguments are explicit or inferable.
    • Generic decorator factories that preserve a concrete decorated function signature.
    • Diagnostics when a generic function reference cannot be inferred or requires unsupported callable polymorphism.
    • Checked API metadata should preserve the decorated function's concrete source signature.
  • Out of scope:

    • Parameter-pack syntax for arbitrary callable transformations.
    • Type-erased decorators.
    • User-defined decorators on aliases, classes, models, traits, fields, or modules.
  • Done when:

    • A generic identity decorator factory can be used on multiple function signatures without defining one factory per signature.
    • Explicit generic instantiation works at decorator sites, or the compiler emits a clear diagnostic explaining why explicit instantiation is required.
    • The feature has parser/typechecker/lowering/emission tests and at least one cross-module decorator-factory regression.

Related: RFC 035 (#169), RFC 036 (#170), and the InQL RFC 014 function registry work.

Metadata

Metadata

Assignees

No one assigned

    Labels

    featureNew feature or requestincan compilerSuggestions, features, or bugs related to the Compiler (frontend/backend/codegen)incan language semanticsSuggestions, features, or bugs related to the Incan Language itself (syntax and semantics)
    No fields configured for Feature.

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions