- Proposal: SE-NNNN
- Authors: Matthew Johnson
- Review Manager: TBD
- Status: Awaiting implementation
This proposal enhances variadic parameters by introducing support for user-defined types, labeled variadic parameters, and a choice at the call site of whether to provide a variadic argument list or a collection value.
Swift-evolution thread: Discussion thread topic for that proposal
Variadic parameters are a nice feature in Swift. They allow us to elide array brackets at a call site. Unfortunately this elision comes with a cost: even though variadics formally take an Array
one cannot be provided at a call site. This often results in an overload set to support direct arrays as well:
func compute(ints: Int...) -> Int {
return compute(ints: ints)
}
func compute(ints: [Int]) -> Int { ... }
Further, Array
is priveleged as the only type that is supported by variadic parameter syntax. This is at odds with Swift's otherwise pervasive support for user-defined types in its literal syntax.
We should reimagine variadic parameters as simple syntactic sugar for optionally eliding collection literal brackets at the call site.
Swift magically turns the current T...
type syntax into [T]
as the type used in the body of a function. Having the Array
assumption baked directly into this syntax means it will not work with other types.
In order to support types beyond Array
the variadic
parameter modifier is used:
func compute(ints: variadic [Int]) -> Int {...}
// callable in any of these ways:
compute(ints: 42, 43, 44)
compute(ints: [42, 43, 44])
compute(ints: arrayOfInt)
All ExpressibleByArrayLiteral
types are supported. The compiler will use the type's init(arrayLiteral:)
initializer to create a value from variadic arguments provided at the call site.
func compute(ints: variadic Set<Int>) -> Int {...}
// callable in any of these ways:
compute(ints: 42, 43, 44)
compute(ints: [42, 43, 44])
compute(ints: setOfInt)
ExpressibleByDictionaryLiteral
types are also supported with a variadic list of labeled arguments. The labels in this list are key literals and the arguments are values in a dictionary literal.
In order to use the type's init(dictionaryLiteral:)
initializer arguemnt labels must be turned into values of type Key
. This proposal includes two ways to do that.
When the key is ExpressibleByStringLiteral
, the argument label can be passed to init(stringLiteral:)
to create a value of type Key
.
func compute(pairs: variadic [String: Int]) -> Int { ... }
// callable in any of these ways:
compute(first: 42, second: 43, third: 44)
compute(pairs: ["first": 42, "second": 43, "third": 44])
compute(pairs: dictionaryFromStringToInt)
Notice that this sugar allows not only the dictionary literal brackets to be ellided, but the string quotes and the eternal pairs
label as well. pairs
is only used when a collection value is passed directly and variadic syntax is not used.
This feature is not limited to Dictionary
:
// assume OrderedDictionary: ExpressibleByDictionaryLiteral
func compute(pairs: variadic OrderedDictionary<String, Int>) -> Int { ... }
// callable in any of these ways:
compute(first: 42, second: 43, third: 44)
compute(pairs: ["first": 42, "second": 43, "third": 44])
compute(pairs: orderedDictionaryFromStringToInt)
When the key is of an enum
type that does not have any associated values keys can be required to match the identifiers of its cases.
enum Foo { case foo, bar, baz }
func compute(pairs: variadic [Foo: Int]) -> Int { ... }
// callable in any of these ways:
compute(foo: 42, bar: 43, baz: 44)
compute(pairs: [.foo: 42, .bar: 43, .baz: 44])
compute(pairs: dictionaryFromFooToInt)
Notice that this sugar allows not only the dictionary literal brackets to be ellided, but the dot prefix on the enum case identifier and the eternal pairs
label as well. pairs
is only used when a collection value is passed directly and variadic syntax is not used.
This feature is not limited to Dictionary
:
enum Foo { case foo, bar, baz }
func compute(pairs: variadic OrderedDictionary<Foo, Int>) -> Int { ... }
// callable in any of these ways:
compute(foo: 42, bar: 43, baz: 44)
compute(pairs: [.foo: 42, .bar: 43, .baz: 44])
compute(pairs: orderedDictionaryFromFooToInt)
TODO
TODO
TODO
TODO
TODO
TODO