Swift could make variadic parameter expressions more powerful by allowing repeated sequences of parameters in method definitions.
Here's a variadic function definition and call that is valid in Swift 5.7 syntax:
func myFunction(_ value: (name: String, age: Int)..., and otherName: String) {}
myFunction((name: "Ada", age: 26), (name: "Bob", age: 21), and: "Carl")
However, I believe the same definition/call should have the ability to look like this:
func myFunction(name: String..., age: ...Int, and anotherParameter: String) {}
myFunction(name: "Ada", age: 26, name: "Bob", age: 21, and: "Carl")
This could be a new language feature called a variadic sequence where any number of sequenced arguments can be included in a method definition. Note the new ...Int
syntax: this represents the end of a variadic sequence. This feature gives developers a friendlier experience when writing method calls.
As for obtaining the variadic sequence within a function implementation, the dollar-sign syntax may be used. Alternatively, each varaidic sequence element can be obtained as an array of tuples via argument name. Finally, variadic sequence can be iterated through using a for in
loop [1].
func myFunction(a: String..., b: ...Int) {
let allSequences: [(a: String, b: Int)] = $0
let allValuesOfA: [String] = a
let allValuesOfB: [Int] = b
for myA, myB in $0 {
let aValue: String = myA
let bValue: Int = myB
}
}
Proposed Specifics
- Every variadic sequence begins and ends with open and close parameters. Open parameters use the
Type...
syntax. Close parameters use a new...Type
syntax.
func myFunction(_ open: String..., _ close: ...String) {}
- Variadic sequences can contain middle parameters. The type of a middle parameter uses normal syntax:
// Variadic sequence with 3 elements
func myFunction(_ open: String..., _ middle: String, _ close: ...String) {}
// Variadic sequence with 4 elements
func myFunction(_ a: String..., _ b: String, _ c: String, _ d: ...String) {}
- Variadic sequences can use any types desired.
func myFunction<T>(_ a: String..., _ b: Double, _ c: ...T, _ d: Int) {}
myFunction("Swift", 5.7, objectA, "Objective-C", 2.0, objectB, 1234)
- Calls to functions with variadic sequence parameters can include as many repetitions as desired:
myFunction("A", 1, "B", 2, "C", 3, "D", 4) {}
- Varadic sequence elements may be named [2]:
func myFunction<A, B>(property path: KeyPath<A, B>..., _ middle: Comparison, value: ...B) {}
myFunction(property: \.name, .equals, value: "Ada", property: \.age, .greaterThan, value: 21)
- Multiple variadic sequences of varying lengths may be present in a method definition. In this case, each respective sequence is obtained using
$0
,$1
,$2
, etc.:
func myFunction(_ a: String..., _ b: ...Int, _ c: Double..., _d: Date, _ e: ...Int) {
let abValues: [(String, Int)] = $0
let cdeValues: [(Double, Date, Int)] = $1
}
- Variadic sequences can be used alongside regular parameters, and existing variadic arguments:
func myFunction(_ a: String..., _ b: ...Int, _c: Double, _ d: Int...)
myFunction("Ada", 26, "Bob", 21, 5.7, 1, 2, 3, 4)
Restrictions
- One may not create a varadic argument that is itself a variadic sequence.
- Variadic sequences may not contain any varadic elements.
// Two invalid syntaxes
func myFunction(_ invalidOpen: (String...)..., _ close: ...String)
func myFunction(_ open: String..., _ middle: String, _ invalidMiddle: String..., _close: ...String)
Remaining Hiccups (ideas/help needed!)
- I believe there should be some way to name variadic sequences. But doing so would require developers to learn another syntax rule (in addition to the feature's basic
...Close
syntax). Moreover, I cannot imagine of a syntax that maintains Swift-like appearance. Here is my thought process:
// We aim to give this variadic sequence some name:
func f(_ a: String..., _ b: ...Int) {}
f("A", 1)
// Here's an attempted syntax:
func f(vals: _ a: String..., _ b: ...Int) {}
f(vals: "A", 1)
// The issue here is that the developer must learn the 'double: name:' syntax.
// Besides, this syntax isn't very developer-friendly.
// If we try to name sequence element parameters...
func f(vals: a: String..., b: ...Int) {}
f(vals: a: "A", b: 1)
// Again, it looks a bit messy.
-
I believe the dollar-sign syntax
$0
(see [1]) used to obtain the variadic sequence array within a function's implementation may cause issues with Swift's syntax. This syntax is usually only seen within closures. I believe this issue could improved by either:- Using a special keyword to replace
$0
. - Using a variadic sequence name as mentioned in the first hiccup.
- Using a special keyword to replace
-
The value of the
$0
variadic sequence ([(TypeA, ..., TypeB)]
), an array of tuples, may have a naming clash issue. Would the argument name or parameter label be used in the tuple? Consider:
func f(a valA: String..., b valB: ...Int) {
// Is $0 of type (a: String, b: Int) or (valA: String, valB: Int) ?
let valueA: String = $0.a // Clash!
let valueA: String = $0.valA // Clash!
}
- The purpose of the following bullet point asks about the parity with Swift's existing Variadic Argument feature:
func f(a: Int...) {}
f(a: 1, 2, 3, 4) // The name label 'a' appears once.
If a variadic sequence can have named elements (see [2]), should only the first sequence of elements passed to a function call use names? Here is the question illustrated:
func f(name: String..., age: ...Int) {}
f(name: "Ada", age: 26, name: "Bob", age: 21) // Names always present
f(name: "Ada", age: 26, "Bob", 21) // Names on first sequence only
Closing
Please let me know if you have any ideas for my list of hiccups. Do you think this feature has the potential to be a part of the Swift Evolution process? Do you have any thoughts or improvements? I will be monitoring the comment thread! If this gets traction, I'll build it in a Swift fork.
Thanks,
Ben