Can I not pass a parameter that is simply any Sequence?

I have this method on one of my objects:

func purging(_ victims:Set<ValveSpan>) -> Schedule {
    return Schedule(spans: self.spans.filter { span in victims.contains(span).NOT } ).rationalized()

The only method that is used on the victims parameter is contains. So while the original use case had a Set handy, there's cases where I'd like to just be able to use an Array there as well. So I thought I would modify the method to be more general. Since contains comes from the Sequence protocol that Set conforms to, I tried:

func purging(_ victims:Sequence<ValveSpan>) -> Schedule { ...

But this resulted in:

Cannot specialize non-generic type 'Sequence'

Which isn't really helpful for me personally. But I tried it with just Sequence as well. Which just produces the:

Protocol 'Sequence' can only be used as a generic constraint because it has Self or associated type requirements

Can one not have an arbitrary collection type as a parameter? What simple syntax twist am I missing? Or do I just have to have to pick a concrete collection type and force all call sites to create that specific type if need be?

What you're seeing here is a restriction because Sequence is a protocol with associated types. There's a distinction between generic protocols, i.e. 'A sequence of ValveSpans', and protocols with associated types, which is asking for 'a type that is a Sequence whose Element is ValveSpan'. Your intuition, 'do I just have to have to pick a concrete collection type' is almost correct; you can use generics to write a function that accepts any type that conforms to the Sequence protocol whose Element is ValveSpan.

The syntax for this is:

func purging<VictimSequence: Sequence>(_ victims: VictimSequence) where VictimSequence.Element == ValveSpan

For more information, check the Swift Programming Language Book's section on Generics:
There is a subsection on associated types.

1 Like

Thank you! I had to figure out where the -> return type goes... BEFORE the where clause apparently.

If I have a bunch of methods that all work with arbitrary collections of ValveSpans, is there some way to make a protocol type that represents "an arbitrary collection of ValveSpans" that DOES NOT have the problem of associated types? Or do I have to repeat this boilerplate generics everywhere?

(I have read the generics section twice; but to date, I find I still really struggle with Swift's generics stuff)

We don't yet support defining such a protocol as a type, but you have a couple of options for expressing these operations. You could make them methods in an extension on Sequence:

extension Sequence where Element == ValveSpan {
  func purging(_ victims: Self) -> Self { ... }

Or you can define a protocol that refines Sequence and adds a constraint:

protocol ValveSpanSequence: Sequence where Element == ValveSpan {}

extension Array: ValveSpanSequence where Element == ValveSpan {}

func purging<S: ValveSpanSequence>(_ victims: S) -> S { ... }