protocol myProtocol {}
func doSomething(with params: (some myProtocol)...) {
// Implementation goes here
}
extension Int: myProtocol {}
doSomething(with: 1, 2, 3)
Compilation error at the func declaration line:
'some' types are only permitted in properties, subscripts, and functions
I could simply omit the keyword altogether, but then it is considered to be 'any' by default. Consequently, I cannot pass the params to a function that expects [some SyntaxProtocol]
as a parameter.
some
in variadic parametersDisallowing 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
So, it's not some bug, or oversight. It's working as intended.
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 protocolP
. 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)
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).