Variadic types: "Same-element requirements are not yet supported"

I was really excited to replace a lot of my repetitive declarations like:

func combineLatest<B: Publisher, C: Publisher, D: Publisher, E: Publisher>(_ b: B, _ c: C, _ d: D, _ e: E) -> AnyPublisher<(Output, B.Output, C.Output, D.Output, E.Output), Failure>
    where Self.Failure == B.Failure, B.Failure == C.Failure, C.Failure == D.Failure, D.Failure == E.Failure { ... }

func combineLatest<B: Publisher, C: Publisher, D: Publisher, E: Publisher, F: Publisher>(_ b: B, _ c: C, _ d: D, _ e: E, _ f: F) -> AnyPublisher<(Output, B.Output, C.Output, D.Output, E.Output, F.Output), Failure>
    where Self.Failure == B.Failure, B.Failure == C.Failure, C.Failure == D.Failure, D.Failure == E.Failure, E.Failure == F.Failure { ... }
// repeat, adding more parameters up to nth arity...

With one declaration of something like:

func combineLatest<each PublisherType: Publisher>(_ publishers: repeat each PublisherType) -> AnyPublisher<(Output, repeat (each PublisherType).Output), Failure> where repeat  Failure == (each PublisherType).Failure { ... }

But I get the error: Same-element requirements are not yet supported
That 'yet' makes it seem like this is a limitation that might be lifted someday.
Is this part of a proposal that didn't get implemented completely, or is it an intentional limitation?
To ask another way, are there any concrete plans to lift this someday? Or would this require an evolution proposal to implement?

4 Likes

I think I know how to fix the outstanding issues mentioned in the PR description, I just need to find a little time to get it over the finish line :slightly_smiling_face:

The design of same-element requirements is specified in SE-0393.

10 Likes

That's great news! Thanks for your work on this.

Wow, interesting. I was having a hard time figuring out which variadic proposal was the relevant one. Thanks for the link :slightly_smiling_face:

2 Likes

Hey @hborla, I am aware that same-element requirements is not implemented yet. But once it is, would it be possible to require all elements of a pack to be of the same type without caring which type it is? For example:

struct Container<each S: Sequence> where repeat (each S).Element == ??

That is:

let numbers: [Int] = [1, 2, 3]
let bag: Set<Int> = [4, 5, 6]
// The following should be allowed
let containerA = Container(numbers, bag)

let names: [String] = ["Holly", "James", "Marcos"]
// The following won't compile because the elements are not of the same type
let containerB = Container(numbers, bag, names)

From the linked proposal:

func variadic<each S: Sequence, T>(_: repeat each S) where repeat (each S).Element == T {}

You must make your type/function generic over the pack and the element type.

3 Likes

Right, I was missing the part of making the variadic type generic over the element type as well. Thank you for the reply.

struct Container<E, each S: Sequence> where repeat (each S).Element == E
2 Likes

@hborla By any chance were there plans to continue shipping 67465? Or are there plans for a different workaround or solution to same element requirements? Thanks!

My PR has been subsumed by [Requirement Machine] Implement same-element requirements. by simanerush · Pull Request #70227 · apple/swift · GitHub which is actively being worked on by @simanerush.

10 Likes

Thank you @hborla and @simanerush! I just hit this limitation today, and am happy to see it's actively being worked on :clap:

2 Likes

Very cool to see progress on this. My use case is having a function with a variadic number of arguments of the same type, i.e. something that accepts

() -> Bool
(Bool) -> Bool
(Bool, Bool) -> Bool
...

What I would love to write would be

func f<each B>(_ g: (repeat each B) -> Bool)
where
  repeat each B == Bool { ... }

My current workaround is to have a protocol for values that can be converted to/from Bool and then to require each B to conform to this, but of course it would be nice to have the ability to express this directly (also since it would likely play better with type inference, { $0 } currently cannot be inferred as (Bool) -> Bool)

If the values are all the same type, can you use non-generic variadic parameters?:

func f(_ g: (Bool...) -> Bool)

Yes, that would be an option. I would like to statically determine the arity of g in the f's body, though, which would require runtime reflection if I am not mistaken.

Oh, interesting. I didn't know that was possible with parameter packs