I've made a few more edits to the draft to add detail to a few parts of the proposal. I think this is approaching a final draft, but I am still interested in hearing any and all feedback.
Regarding the explicit vs. implicit discussion: I don't see a way forward for this specific proposal which doesn't involve an explicit syntax marker. I think any approach which relied on implicit conversion would be best developed as a separate pitch/proposal, as the specification would be substantially different.
Rather than coming up with a new operator or syntax for this, I'd lean towards re-using as.
func foo(_ args: Int...) { }
let array = [1, 2, 3, 4, 5]
foo(array as Int...)
I'm on the fence about whether or not it should only be required only in the ambiguous cases (such as print) or if we should just require the as T... in all cases. Either choice is probably reasonable.
I agree. Since the only good operator (IMO) (f(x...)) is off limits ~for ABI compatibility~, I'd prefer to see this feature built from existing keywords/syntax. as T... isn't long (shorter, actually, than #variadic(_) depending on the type name) and does what it looks like it does — it would be easy to learn and adopt.
Unless there is a good reason for requiring as T... in unambiguous cases, I'd prefer not to be required to specify it.
Is there a reason this can't match the current behavior in print where it warns you when there's an implicit conversion to Any?
Slightly tweaking my previous post...
func f(x: Any...) { }
// Matches current behavior
f(x: [1, 2, 3]) // Warning: Expression implicitly coerced from '[Int]?' to 'Any' *fixits*
//Fixed:
f(x: [1, 2, 3] as Any) // passed as variadic Any... with a single entry
// or
f(x: [1, 2, 3] as [Any]) // passed as a variadic with 3 entries
// or
f(x: [1, 2, 3] as Any...) // passed as an array of 3 entries
It seems like it should be possible to match the current behavior when there's ambiguity and simply provide warnings/fixits for that ambiguity. In this case only new code would even be able to take advantage of the conversion.
IMO it seems like an unnecessary compromise to introduce any new syntax to do something that, (from a user perspective), is an obvious conversion. When there's ambiguity, point it out and ask the user to clarify the same way as is done in the rest of the language. When there's not, (which IMO would be the majority of time) why add an additional thing for users to remember?
Also, FWIW it seems like most of the talk/examples are using Array literals. Most of the time this has a been a pain point for me is wanting to pass an existing array as a variadic parameter.
I've been working on a more detailed summary, but I think the strongest argument against using the as T... syntax is how it might be used alongside other casting/coercion operators. For example, if that syntax is used I'm not sure the following should be allowed:
let x: Any = ...
foo(bar: x as! Int...)
because the as? counterpart wouldn't make much sense:
let x: Any = ...
foo(bar: (x as? Int...) ?? /* no real way to provide a fallback value */)
If we adopted this syntax, I think we'd want to disallow the use of as? and as!, and require that the lhs is always an array if the rhs is a variadic type.
Also, this use of as would be somewhat unique in that it would only be allowed in the argument position.
I don't think those are good enough reasons to dismiss the as T... syntax, but they're worth thinking about.
I do plan on keeping a discussion of syntax alternatives in the final proposal in case opinions change if and when this comes up for review. FWIW, it seems like the frontrunners right now are #splat and as T...
I think there's a pretty clear consensus now that as T... would be preferable to using a pound prefixed keyword for this feature. I've updated the proposal to reflect that. I think there are still a few details to be discussed in the semantic restrictions section, but I don't anticipate major blocking issues.
Note the current proposal actually disallows this, because it introduces a copy the programmer might not expect. Currently, the correct solution would be to concatenate arrays before splatting (explicitly calling out the allocation/copy in the source)
Be that as it may, but it is however, a term of art. It's used across many programming languages.
However, it's of course possible to cal it the "spread operator" instead of the "splat operator", both are commonly used. And we can use as T... as a splat operator without explicitly giving it a name.
However, unprofessional or not, and wether we decide to use or not, it is still a fairly common term.
"Splat" is a slang term for an asterisk (because it looks like something that has been squished against a surface); the languages that call this feature "splat" use prefix * for it. I don't know of a language that actually writes out "splat" in source code.
Do you think it would be worth considering having a Set<T> as T... syntax for type signature as a future direction that can add additional types that do conform to the expressible by array literal protocol?
Or should it be the other way around T... as Set<T>?
func foo<T: Hashable>(_ set: T... as Set<T>) { ... }
I'm intentionally avoiding adding things like arbitrary ExpressibleByArrayLiteral support as future directions right now as they're mostly orthogonal to this proposal, and there's no broad consensus on the design/scope.
I'm not a fan because it sounds like it might be a destructive operation, but I can see how it's arguably a term of art. I added it as one of the proposed alternate spellings along with some other minor revisions.