Enhanced Variadic Parameters

I am talking about what @Nevin has mentioned as well as the issue about retroactively adding a variadic overload that was mentioned earlier.

It is not clear—to me, at least—that the semantics of avoiding a forward overload is very popular. Perhaps I missed a previous discussion on it?

In any case, even if it is popular, since what is proposed is effectively a change (see my previous message), I’d argue that it has to meet the bar that the current behavior should be proved actively harmful; it’s not enough to say that, were we to do it over, we’d prefer an alternative.

I’m well aware that adding new syntax first and then applying new semantics is source-breaking. The implication, therefore, of my argument that the issues are separable is that I think we need to have a standalone discussion on changing variadic semantics before consideration of new syntax and not alongside it. This has the benefit that, if it clears the bar of showing the existing behavior is actively harmful (which is not implausible, if the argument you’re advancing is that the behavior you promote is the one that users actually expect at the callsite), then a case can be made that the same semantics should be applied to the existing syntax with appropriate automatic migration of source code.

If the new semantics can’t clear that bar, I’d argue that this proposal should not attempt to introduce such semantics.

That is a reasonable distinction, certainly. Given that there is a connection, it is worth a discussion to my mind.

No, but it is up to the community to provide feedback, which includes consideration of whether any pitches are consistent with the feel and direction of Swift. Since you’re soliciting feedback, my feedback to you is as above.

1 Like

Separating the two aspects is one possibility. However, I don't agree with the implication that you can break down every proposal into its separable parts and there is some sort of conservation law where each one should be able to “clear the bar” on its own. Some things will only be deemed worth doing if they're bundled with other changes. In this case, it might not be worth making the change to how variadics work while keeping the same syntax, but if a new syntax for variadics is already desirable because it opens up other possibilities (e.g. changing the collection type as mentioned here), then this might tip the balance for other tweaks to the design at the same time (particularly as it preserves the semantics of existing code and makes a deprecation cycle possible).

Also, more specifically, the new proposed syntax of variadic [T] or similar strongly implies that you can also pass an array as an argument directly, whereas the existing T... hides the underlying [T] nature of the parameter.

1 Like

Is the fact that we cannot write decent* wrappers to APIs that are offered only as variadic (e.g. os_log) considered harmful enough?

* without writing the same wrapper manually for each sensible number of arguments

2 Likes

Okay, here’s a thought:

The situations where there is ambiguity between T... and [T] are quite limited. It can only happen when [T] is a subtype of T (the vice-versa cannot occur: any inhabitable subtype of [T] would itself be an array, hence there would be recursive nesting T == [[[...]]] which is not a well-formed type; and if T is uninhabited, eg. Never, then it’s impossible to actually pass in an instance).

So, when is [T] a subtype of T?

The only supertypes of [T] (ie. Array<T>) are protocols it conforms to. And if T is a protocol, then a value of type T (the existential) only conforms to T (the protocol) if T is a self-conforming protocol. Thus, the ambiguity with variadics can only arise for self-conforming protocols.

Currently, we have exactly two of those in Swift, which are called Any and Error. In the future we might add more, or make it possible for developers to define their own, but today there are only two.

If someone writes “extension Array: Error where Element: Error {}”, then a function which takes a variadic Error parameter can be called with a value of type Error, and it can also be called with a value of a type that conforms to Error, such as [Error].

The Any case is similar, and was described up-thread.

• • •

All of this is to say, the scope of the potential ambiguity is extremely limited. It only arises with self-conforming protocols, and we only have two of those.

I think it would be reasonable to make variadic functions “just work” and accept array arguments directly *unless it would be ambiguous*. In other words, unless T is a self-conforming protocol.

Currently, variadic functions *cannot* be passed an array to treat as several arguments. We could lift most of that restriction and say, “Variadic functions of a self-conforming protocol cannot be passed an array to treat as several arguments.”

Then, perhaps at a later time, we could consider some way to explicitly pass an array-of-arguments to self-conforming variadics. One motivating use-case here is to write wrappers around print, which takes an Any....

1 Like