Another attempt at passing arrays as varargs (with implementation)

I'm not a big fan of * as it doesn't transmit the meaning to me, but thanks for updating the proposal clarifying why ... is discarded. For people like me with some experience in other languages that use ... it would make it hard to not ask ?why not? on the proposal review :laughing:

FWIW, to the extent that the revised proposal's dismissal of as ... relates to what I suggested above, the reasoning doesn't actually apply.

I did not propose as ... as shorthand for as T.... I proposed the syntax as ... as syntax to indicate splatting. There is no need for consistency with as <sometype>, and there is no problem with heterogenous collections.

Taking a wider view, I'm also unhappy that the proposal treats "splat" as an operator, when it clearly isn't. Specifically, *<array> (or however it's spelled) as an expression has the same type and value as <array>.

What we're trying to do, surely, is to come up with syntax to treat an array as a list in parameter position. Trying to formalize this as an expression (via an operator) seems misguided to me, especially because that promotes artificial constraints on the design of the feature — and ends up complexifying both syntax and usability.

Of course, it's perfectly fine for the implementation of this feature to treat it as a pseudo-operator limited to specific contexts, if that's the most natural path to a solution. But let's not deform the feature because of that.

1 Like

if ... is not intended to represent a type in that case, I don't think it makes sense to use it with the type coercion operator.

I think it's important to note, the restrictions imposed on this new operator are the same ones imposed on & today when working with inout arguments.

1 Like

What about

foo(array as Variadic)

? Since we can do as Optional and the likes. This would also add future direction where Variadic is a simple Collection subprotocol should we want to support other types.

1 Like

There are currently 3 operators that involve the word as (as, as? and as!):

https://docs.swift.org/swift-book/ReferenceManual/Expressions.html
(under the heading "Type-Casting Operators")

I'm suggesting a fourth (as..., although I'd prefer that internal whitespace be allowed, so as ... too).

If it's an operator, it's not a "type-casting" operator, but rather a "type-reinterpretation" operator, or something like that. I don't see why the semantics are a bar to spelling it using as — if the community likes the spelling.

3 Likes

I thought the previous as T... syntax was good, because it was similar to toll-free bridging (cf. as CFArray and as NSArray).

Here's an alternative to the prefix * operator, using three commas after the variadic parameter/argument.

Variadic parameter:

public func print(_ items: Any,,,)

public func print(_ items: Any,,,
                  separator: String = " ",
                  terminator: String = "\n")

Variadic argument (splatting):

let items: [Any]

print(items,,,)

print(items,,, separator: "\t")

Notes:

  • There are three trailing commas when the last parameter/argument is variadic.

  • There are only three (not four) commas when a variadic parameter/argument is followed by another parameter/argument.

I still don't see a case for an operator being required to do something that's basically a compiler/syntax constraint.

I understand having caution around implicit conversion, but that's not what's happening with [T] -> T.... T... is not a Type. At least not (currently) in Swift. It's syntactic sugar that allows a variadic number of parameters. The Type of T... is functionally (and literally other than at the call site) [T]

The only ambiguity in T... is when T and Array share an underlying Type. The main pain point here is print() and can be handled the same way as passing an Optional to Any...: default to the most base-Type unless specifically cast by the User, with an accompanying warning about the ambiguity. I believe this would match the current behavior and shouldn't cause any source compatibility issues.

This is coming from a usage perspective and I'm guessing it complicates things in regards to implementation, but IMO passing an array as a variadic is clearly ergonomically the best at the call point.

12 Likes

Edit: made a few more revisions, and added an example involving overrides of methods which take varargs

I support this proposal, and the use of the * operator syntax, eg, *args.

@owenv What's the status on this proposal? I've seen a couple other tentative proposals floating around on the forums, but nothing definitive. Has anyone from the core team commented on this proposal or any of the others like it? We NEED a splat operator in swift. I would prefer using the * symbol, but I would be satisfied with anything as long as it gets implemented.

2 Likes

This is on hold until the design of variadic generics is farther along. The consensus is we shouldn't make smaller incremental improvements like this one until we're confident they won't limit the design space for more general features.

1 Like

What's slated to change about variadic generics? Are you simply referring to the fact that functions/methods can use variadic parameters with generic types, as in

func test<T>(items: T...) { ... }

or something else?

As I understand, variadic generics are something like this:
func item<T1, T2, ..., Tn>(item1: T1, item2: T2, ..., itemN: Tn)

3 Likes

@kireev165
Could you provide a more detailed example? I'm not sure how the above function would work.

More examples could be found in Generics Manifesto.

Any progress?

2 Likes

Looking forward to this! :)

In my opinion, any syntax for converting an array to a variadic adds completely unnecessary complexity to the language and pollutes the syntax. It seems to me that the only adequate and intuitive way is to allow the array to be passed where the variadic is expected. I think even if an operator is added to Swift, I will continue to duplicate the methods.

2 Likes

Being able to pass a collection/sequence where variadic is expected, feels like the most simplistic organic approach. That would help greatly simplify the call site where variadic is expected. It confuses me that this wouldn't be the approach since using a parameter that uses variadic is represented as a sequence, or at least it appears to be, since it's applied to another collection with the -append(contentsOf:) method, as well as accessing an element requires using for each.