Why you can't use some
in variadic parameters
Disallowing usage of opaque types(some Protocol
) in variadic parameters was a conscious decision by the language developers. Because it would conflict with another proposed language feature called variadic generics
:
Variadic generics
An opaque type cannot be used in a variadic parameter:
func acceptLots(_: some P...)
This restriction is in place because the semantics implied by this proposal might not be the appropriate semantics if Swift gains variadic generics. Specifically, the semantics implied by this proposal itself (without variadic generics) would be equivalent to:
func acceptLots<_T: P>(_: _T...)
where acceptLots
requires that all of the arguments have the same type:
acceptLots(1, 1, 2, 3, 5, 8) // okay
acceptLots("Hello", "Swift", "World") // okay
acceptLots("Swift", 6) // error: argument for `some P` could be either String or Int
With variadic generics, one might instead make the implicit generic parameter a generic parameter pack, as follows:
func acceptLots<_Ts: P...>(_: _Ts...)
In this case, acceptLots
accepts any number of arguments, all of which might have different types:
acceptLots(1, 1, 2, 3, 5, 8) // okay, Ts contains six Int types
acceptLots("Hello", "Swift", "World") // okay, Ts contains three String types
acceptLots(Swift, 6) // okay, Ts contains String and Int
Source: https://github.com/apple/swift-evolution/blob/main/proposals/0341-opaque-parameters.md#variadic-generics
So, it's not some bug, or oversight. It's working as intended.
What is the meaning of that some
keyword in parameter type declaration anyways?
But what should you do with your code? Well, we gotta check why that language feature was added in the first place, and what it is doing "under the hood":
And it was introduced to make function with heave usage of generics "lighter" and easier to read.
To turn code like:
func horizontal<V1: View, V2: View>(_ v1: V1, _ v2: V2) -> some View {
HStack {
v1
v2
}
}
into this:
func horizontal(_ v1: some View, _ v2: some View) -> some View {
HStack {
v1
v2
}
}
Both of those snippets are equivalent, version with some View
during compilation just turns into generic function version.
As with opaque result types, some P
indicates a type that is unnamed and is only known by its constraint: it conforms to the protocol P
. When an opaque type occurs within a parameter type, it is replaced by an (unnamed) generic parameter. For example, the given function:
func f(_ p: some P) { }
is equivalent to a generic function described as follows, with a synthesized (unnamable) type parameter _T:
func f<_T: P>(_ p: _T)
Source: https://github.com/apple/swift-evolution/blob/main/proposals/0341-opaque-parameters.md#proposed-solution
What should you do
As it was said before: this synctactic sugar is explicitly unavailable for variadic parameters. So, only obvious solution would be to fall back to the "unsugared" syntax and use generic parameters:
func doSomething<P: myProtocol>(with params: P...) {
// Implementation goes here
}
And it will work as intended (if you intended for that all parameters in variadic list should have exactly the same type, that confirms to myProtocol
, ofc).
some
for function parameters was only allowed recently (last year IRRC) to simplify code likefunc foo<T: Proto>(bar: T)
tofunc foo(bar: some Proto)
. Internally they're basically the same. So, when in trouble just use explicit generic form.some myProtocol
in variadics or arrays makes it ambiguous whether you want all the value have the same type that conforms tomyProtocol
, or you want values of different types, but all of them conform tomyProtocol
.some
for parameters feature introduction? It's here: github.com/apple/swift-evolution/blob/main/proposals/…