marek094 marek094 - 11 months ago 180
Swift Question

Swift3: type inference inside generic extension

I need to do something like this:

extension Array {
func flat() -> Element { return self.flatMap { $0 } }

But there is problem with type inference:

'flatMap' produces '[SegmentOfResult.Iterator.Element]', not the
expected contextual result type 'Element'

Edit: Usage example:


should produce
which is the same as:

[[1,2],[3,4,5],[6]].flatMap { $0 }

Answer Source

If you take a look at the flatMap(_:) signature,

extension Sequence {
    // ...
    public func flatMap<SegmentOfResult : Sequence>(_ transform: (Self.Iterator.Element) throws -> SegmentOfResult) rethrows -> [SegmentOfResult.Iterator.Element]
    // ...

you'll see that it returns a [SegmentOfResult.Iterator.Element], where SegmentOfResult is the type that is returned from the function you pass it. This isn't necessarily the same type as Element (as your extension is for all arrays), which is why your code fails to compile.

In order to be working with arrays where the elements are sequences, you'll want to constrain your extension so that Element : Sequence.

Also, as the function you're passing to flatMap(_:) is an identity transform (it just returns the element it receives as input), you'll want to change the return type to [Element.Iterator.Element] (an array of the inner element).

extension Array where Element : Sequence {
    func flat() -> [Element.Iterator.Element] {
        return self.flatMap { $0 }

Although that being said, I see no reason why this shouldn't be an extension of Sequence:

// An extension for a sequence of sequences
extension Sequence where Iterator.Element : Sequence {

    // returns an array of the inner element type (an array of the element of the element)
    func flat() -> [Iterator.Element.Iterator.Element] { 
        return self.flatMap { $0 }

(However, I don't really see the need to create an extension for this in the first place – array.flatMap{$0} isn't exactly lengthy!)