Variadic initializers can't be default initializers?

Here's my code:

extension MyType: SetAlgebra {
  //...

  public init<each S: StringProtocol>(_ string: repeat each S) {
    var result = [String]()
    for s in repeat each string {
      result.append(s.decomposedStringWithCanonicalMapping)
    }
    self.init(result, alreadyDecomposed: true)
  }
}

The compiler wouldn't accept the zero-argument version of this method to satisfy the default initializer requirement. Am I missing something, or is this some sort of whoops over coverage?

1 Like

You are not missing anything. Swift doesn't ever work that nicely, parameter packs or no.

protocol P { func f() }

struct MyType: P { // ❌ Type 'MyType' does not conform to protocol 'P'
  func f(_: some Any = ()) { }
}

P.S. You don't need a loop.

repeat result.append((each string).decomposedStringWithCanonicalMapping)
2 Likes

I kind of mis-worded it. My case doesn't involve default argument values at all. In the past, we had issues where enumeration cases couldn't fill in something expecting a type-level property, although they have the same user interaction model. Variadic members should be able to satisfy requirements if one of their variations can match.

I think you worded it fine. But I don't think SE-0280 is a good match, because enum cases always had one concrete signature.

Parameter packs currently behave like default arguments, instead of like generics:

protocol P { func f(_: Int) }
struct MyType: P { // Compiles.
  func f(_: some Any) { }
}

The list here of what works is still a good reference:

That article says it should work!

  • Generic functions can satisfy non-generic protocol requirements

Or is it just non-variadic generic functions?

Now at Variadic generic member functions can't satisfy protocol requirements. #89120

That problem doesn't require parameterless ("default" for parameterless is not Swift terminology, and "variadic" is used for "...", not parameter packs now) functions.

However, while investigating, I turned up this inconsistency, which is specific to zero-parameter functions and ones that takes instances of Void. It probably needs to be fixed in order for your use case to be supported as well eventually.

:white_check_mark:: Compiles
:cross_mark:: Crashes Compiler

typealias F<each T> = (repeat each T) -> Void

func f<each T>(_: repeat each T) { }
_ = f as F<> // âś…
_ = f as F<()> // ❌

enum E {
  static func f<each T>(_: repeat each T) { }
}
_ = E.f as F<> // ❌
_ = E.f as F<()> // âś…

The type alias is not necessary but reduces repetition of -> _.