Roman
Roman

Reputation: 1500

"some" keyword with variadic parameter in Swift

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.

Upvotes: 0

Views: 250

Answers (2)

user28434'mstep
user28434'mstep

Reputation: 6600

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).

Upvotes: 2

Joakim Danielson
Joakim Danielson

Reputation: 52043

Use generics to get the same result as some and only allow one specific type that conforms to the protocol

func doSomething<ProtocolType: myProtocol>(with params: (ProtocolType)...) {
    // Implementation goes here
}

Upvotes: 3

Related Questions