4

I would like to extend the Array class, but only for elements which conform to the SequenceType protocol.

The code I would like to write / the code I think would be sensible is:

extension Array {
    func flatten<T: SequenceType>() -> [T.Generator.Element] {
        var result = [T.Generator.Element]() // *** [2]

        for seq: T in self { // *** [1]
            for elem in seq {
                result += [elem]
            }
        }

        return result
    }
}

However, Swift seems to create two versions of T. I assume one comes from the normal Array declaration, and the other from the attempted constraint on my method.

In particular, this means that line [1] gives the lovely error 'T' is not convertible to 'T'.

Similarly, the line at [2] fails, because in that context Swift does not recognise T as having a .Generator member (although the return type annotation in the declaration is valid).

I've tried a number of other formulations (including using, eg, T where T: SequenceType), but I can't seem to find a way to express this constraint. Is this possible, or am I barking up the wrong tree?

4
  • I hope so, but I don't think so. Commented Aug 8, 2014 at 0:09
  • 1
    An alternative would be func flatten<T: SequenceType>(array: [T]) -> [T.Generator.Element] {... Commented Aug 8, 2014 at 0:17
  • @jtbandes - I actually thought the same, but trying for a generic version of that function crashes my compiler >< (stackoverflow.com/questions/25194210/…) Commented Aug 8, 2014 at 0:18
  • Not as of a beta or two ago. Commented Aug 8, 2014 at 0:29

2 Answers 2

2

The answer to this, as commenters to your question have already stated, is no. However, it's pretty clear from the organization of the Swift standard library that writing a specialized generic function as a method is discouraged. (I suspect that will change as Swift's type system evolves.) For instance, notice that there is no contains method of Swift's native array type. Instead, it is implemented as a global function, because of the requirement that elements be Equatable. This is precisely because of the limitation you encountered: It is not possible to specialize a generic type parameter of the enclosing class in a method, whether it's an extension or not. (This is, in my opinion, a truly unfortunate limitation.)

So, as Swift currently stands, I would "implement" flatten by just using join, e.g., join([], arrayOfArrays).

Sign up to request clarification or add additional context in comments.

1 Comment

Yeah, that's a fair point from an architectural standpoint. They seem to have made the decision to go for consistency (aka Python) rather than ease of use/reading (Ruby), which is fair enough.
2

With Swift 2 this is now possible:

extension SequenceType where Generator.Element : SequenceType {
    func flatten() -> [Generator.Element.Generator.Element] {
        var result: [Generator.Element.Generator.Element] = []
        for seq in self {
            result += seq
        }
        return result
    }
}

[[1,2,3],[],[2,4,5]].flatten() // [1, 2, 3, 2, 4, 5]

Note though that this specific function isn't so important now that we have flatMap in the library.

Comments

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.