ZE-004 - Variadic Attributes
Variadic Attributes
This proposal has been rejected and will not be implemented.
Introduction
This proposal introduces variadic attributes to Zirric, enabling attributes and functions to accept a variable number of arguments. This feature addresses the need for multi-instance constraints (e.g., @Requires(Countable, Iterable)) while maintaining a clean and expressive syntax.
Variadic attributes are particularly useful for:
- Representing requirements or constraints on fields, parameters, or types.
- Improving readability and reducing verbosity compared to alternatives like
@HasAll([Countable, Iterable]).
Motivation
Zirric currently lacks a concise way to specify multiple constraints or requirements in attributes. For example, to enforce that a parameter must satisfy both Countable and Iterable, users must use workarounds like @HasAll([Countable, Iterable]), which is less readable and feels unnatural.
This could be useful in scenarios such as:
- Type Constraints: Annotating parameters or fields to require multiple traits or interfaces.
fn process(@Requires(Countable, Iterable) data) {} - Tooling and Compile-Time Checks: Enabling tools (e.g., LSP, compilers) to validate that values meet multiple constraints.
- Library Design: Allowing library authors to define flexible APIs that accept variadic constraints.
Benefits
- Readability:
@Requires(Countable, Iterable)is more intuitive than@HasAll([Countable, Iterable]). - Flexibility: Supports empty lists, optional constraints, and future extensibility.
- Consistency: Aligns with variadic functions in other languages, making it familiar to developers.
Proposed Solution
Syntax
Introduce a @Variadic attribute to mark attribute fields as accepting a variable number of arguments. When used, the field’s type is treated as an Array of the specified element type.
Example: Defining a Variadic Attribute
attr Requires {
@Variadic()
@ArgType(Type) // Accepts types or type-like attributes
constraints
}
Example: Using a Variadic Attribute
// Function with variadic constraints
fn process(@Requires(Countable, Iterable) data) {
// `data` must satisfy both `Countable` and `Iterable`
}
// Valid calls
process([1, 2, 3]) // Array of Int (assuming Int is Countable and Iterable)
process([]) // Empty array
// Invalid call (if String doesn't satisfy Iterable)
process("hello") // Compiler error: String is not Iterable
Behavior
- Type Handling: Variadic arguments are always treated as an
Arrayof the specified element type (e.g.,@ArgType(Type)). - Empty Lists: Supported for optional constraints (e.g.,
@Requires()). - Type Safety: The compiler enforces that all provided arguments match the element type (e.g.,
Type).
Detailed Design
Attribute Definition
- The
@Variadicattribute marks a field within another attribute as variadic. - The field’s type is implicitly an
Arrayof the type specified by@ArgType.attr VariadicExample { @Variadic() @ArgType(Int) // Accepts variadic Int arguments numbers }
Usage in Functions
- Variadic attributes can be applied to function parameters, fields, or types.
fn example(@VariadicExample(1, 2, 3) nums) {} - Functions themselves can also have a variadic parameter syntax similar to attributes.
fn sum(@Variadic() @ArgType(Int) numbers) { // `numbers` is an Array of Int } sum(1, 2, 3, 4) // Valid call
Compiler and Tooling Support
- Parsing: The compiler treats variadic attribute fields as arrays during parsing.
- Type Checking: Ensures all arguments match the element type specified by
@ArgType. - Error Messages: Provides clear errors for type mismatches or invalid usage.
Edge Cases
- Empty Lists:
@Requires()is valid and results in an empty array of constraints. - Single Argument:
@Requires(Countable)is treated as an array with one element. - Type Mismatches: The compiler rejects arguments that don’t match
@ArgType.
Changes to the Standard Library
New Attributes
@Variadic:- Marks a field as accepting a variable number of arguments.
- Only valid within attribute definitions.
@ArgType:- Specifies the type of elements in a variadic field (e.g.,
@ArgType(Type)). - Required for variadic fields to enforce type safety.
- Specifies the type of elements in a variadic field (e.g.,
Example Additions
// Standard library addition
attr Variadic {}
attr ArgType {
type
}
Alternatives Considered
@HasAll([...])Syntax:- Pros: No new syntax required.
- Cons: Less readable and feels like a workaround.
Dedicated Grouping Syntax (e.g.,
@Requires{Countable, Iterable}):- Pros: Clean and intuitive.
- Cons: Doesn’t translate to function arguments and requires new grammar rules.
Multiple Attributes (e.g.,
@Requires(Countable) @Requires(Iterable)):- Pros: Simple and explicit.
- Cons: Verbose and ambiguous for single-instance attributes.
Why @Variadic Was Chosen:
- Balances flexibility and readability.
- Works seamlessly with function arguments and fields.
- Aligns with Zirric’s existing attribute system.
Acknowledgements
- Prior Art: Inspired by variadic functions in languages like C, JavaScript, and Rust.
- Contributors: @vknabel for designing the proposal.
Open Questions
- Should there be spreads to pass arrays as variadic arguments (e.g.,
@Requires(...myArray))? - Should
@Variadicrequire an optionalminormaxparameter or arangeto enforce bounds on the number of arguments?