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

6
  • 2
    some for function parameters was only allowed recently (last year IRRC) to simplify code like func foo<T: Proto>(bar: T) to func foo(bar: some Proto). Internally they're basically the same. So, when in trouble just use explicit generic form. Commented Jan 29 at 13:02
  • Also, it worth noting that using some myProtocol in variadics or arrays makes it ambiguous whether you want all the value have the same type that conforms to myProtocol, or you want values of different types, but all of them conform to myProtocol. Commented Jan 29 at 13:19
  • @user28434'mstep Wow, I didn't know that! Really nice design solution, IMHO. Thank you for info!
    – Roman
    Commented Jan 29 at 15:41
  • @user28434'mstep Can you please provide me with some prooflink? 😳
    – Roman
    Commented Jan 29 at 15:50
  • Prooflink for what? some for parameters feature introduction? It's here: github.com/apple/swift-evolution/blob/main/proposals/… Commented Jan 29 at 16:15

2 Answers 2

3

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
}
3
  • 1
    It would improve the answer if you'll add the reason the feature authors' decided to disallow it fro variadic parameters: github.com/apple/swift-evolution/blob/main/proposals/… Commented Jan 29 at 16:20
  • 1
    @user28434'mstep I think I am going to disagree that we need to explain why something was or wasn’t implemented in the language when posting an answer Commented Jan 29 at 17:07
  • @user28434'mstep Yeah, would be great. But, I guess, nobody knows 😄 Probably, it's even just a bug.
    – Roman
    Commented Jan 30 at 4:44
2

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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.