4

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:

[[1,2],[3,4,5],[6]].flat()

should produce [1,2,3,4,5,6] which is the same as:

[[1,2],[3,4,5],[6]].flatMap { $0 }
marek094
  • 424
  • 2
  • 11

1 Answers1

4

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!)

Hamish
  • 78,605
  • 19
  • 187
  • 280
  • 1
    W.r.t. last paragraph: or, as another native alternative (as previously discussed in a thread I can't find now :), `Array(array.joined())`. Ninja-edit: as discussed in [this Q&A](http://stackoverflow.com/questions/39115192). – dfrib Sep 15 '16 at 18:25
  • & what if I'd like to use `extension Array where Element : Array`? – marek094 Sep 15 '16 at 18:35
  • 2
    @marek094 `Array` isn't a protocol – so that isn't possible (you cannot conform to a concrete type). What you'd be looking for is `extension Array where Element == Array`, which is a [concrete same-type requirement](https://github.com/apple/swift/blob/master/docs/GenericsManifesto.md#concrete-same-type-requirements), which Swift currently doesn't support (there are solutions to work around it that involve tagging with protocols (see for example [this Q&A](http://stackoverflow.com/questions/37977817/extension-for-generic-type-unsafemutablepointeruint8)), but that isn't particulary pretty). – Hamish Sep 15 '16 at 18:39
  • 1
    One way (sort of..) to achieve an extension for 2D arrays is to constrain the `Element` to `Collection`, and constrain `Element.Index` and `Element.IndexDistance` to be `Int` (see for example [this Q&A](http://stackoverflow.com/questions/39179660/swift-2d-array-generic-extension-issue-accessing-2nd-dimension)) – but this won't be exclusive to 2D arrays (just arrays of collections, where those collections are indexable by `Int`), and may require more constraints to do certain things. But for this case, I see no reason in making the extension for 2D sequences, as that's how `flatMap(_:)` is done – Hamish Sep 15 '16 at 18:42
  • @Hamish Thank you for an explanation! – marek094 Sep 15 '16 at 18:57
  • @marek094 Happy to help :) – Hamish Sep 15 '16 at 18:58