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.

11 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

Has anyone got this to work with latest xcode 16.2?
I think I've set the experimental flag but It doesn't seem to make any difference.

let package = Package(name: "Test",
                      platforms: [.macOS(.v15)],
                      products: [
                          .library(name: "Test",
                                   targets: ["Test"]),
                      ],
                      targets: [
                          .target(name: "Test",
                                  swiftSettings: [
.enableExperimentalFeature("SameElementRequirements"),
                                  ]),
                      ])
swift --version
swift-driver version: 1.115.1 Apple Swift version 6.0.3 (swiftlang-6.0.3.1.10 clang-1600.0.30.1)

I have this code that I assume should work but it crashes the compiler (Apple Swift version 6.2-dev (LLVM 162ee50b401fff2, Swift 57288d13c9f3c02) in xcode 16.3 beta 2)

I'm trying to restrict the PropertyType of PropertyAnimatorTuple to be the same as all its elements T. Slight variations of the code gives different compiler errors but the one below crashes the compiler. @simanerush @hborla is this supposed to work?


public protocol TypedAnimateable {
}

extension Int: TypedAnimateable {
}

struct IntProp: PropertyAnimator {
    typealias PropertyType = Int
}

struct OtherIntProp: PropertyAnimator {
    typealias PropertyType = Int
}

public struct PropertyAnimatorTuple<PropertyType: TypedAnimateable, each T: PropertyAnimator>: PropertyAnimator
    where repeat (each T).PropertyType == PropertyType {
        public typealias PropertyType = PropertyType

        let theValue: (repeat each T)
        init(_ value: repeat each T) {
            // self.theValue = (repeat each value)
            fatalError("the line above also doesnt work no matter where I put the parenthesis")
        }
    }

func tester() {
  //  let prop = PropertyAnimatorTuple<Int, IntProp, OtherIntProp>(IntProp(), OtherIntProp()) // this doesnt work. The compiler compains that "Int" and "Int, Int" is not equal.
    let prop: PropertyAnimatorTuple<Int, IntProp, OtherIntProp> = propThing(IntProp(), OtherIntProp())
}


 func propThing<each T: PropertyAnimator, PropertyType: TypedAnimateable>(_ value: (repeat each T)) where repeat (each T).PropertyType == PropertyType -> PropertyAnimatorTuple<PropertyType, each T> {
 return PropertyAnimatorTuple<PropertyType,each T>(repeat each value)
 }

So far the experimental feature allows same-element requirements to be stated in generic signatures but some more plumbing is needed before more complex examples can work.

5 Likes

I’ve tried to use parameters packs a couple of times. I was afraid they might be a bit quirky, but I actually like the design a lot.

However, every attempt has ended up stymied by the lack of same-type constraints. I get the impression that most real-world use cases will face a similar block.

For example, there are several obvious applications within the swift-algorithms library, but they’d all require same-type constraints. I would hazard a guess that this is the reason those APIs haven’t already been converted to use packs.

Anyway, not a complaint! Just fan mail from someone who sees huge potential in packs and who’s eagerly awaiting the day when they reach their potential.

3 Likes

I have hopes that this is being worked at since for example (Pitch: User-defined tuple conformances) depends on same-element requirements. Its a really powerful feature so I'm really looking forward to it.

1 Like