Mapping to collections besides Array

collection
protocols

(Daryle Walker) #1

Would variants of the Sequence mapping methods that don't return Array, but a user-specified RangeReplaceableCollection type be useful?

extension Sequence {

    func compactMap<ElementOfResult, ContainerOfResult: RangeReplaceableCollection>(as: ContainerOfResult.Type, _ transform: (Element) throws -> ElementOfResult?) rethrows -> ContainerOfResult where ContainerOfResult.Element == ElementOfResult {
        var result = ContainerOfResult(), iterator = makeIterator()
        while let e = iterator.next() {
            if let er = try transform(e) {
                result.append(er)
            }
        }
        return result
    }

    func flatMap<SegmentOfResult: Sequence, ContainerOfResult: RangeReplaceableCollection>(as: ContainerOfResult.Type, _ transform: (Element) throws -> SegmentOfResult) rethrows -> ContainerOfResult where ContainerOfResult.Element == SegmentOfResult.Element {
        var result = ContainerOfResult(), iterator = makeIterator()
        while let e = iterator.next() {
            result.append(contentsOf: try transform(e))
        }
        return result
    }

}

I skipped some of the others because I couldn't figure out how to implement them better than calling the Array-returning version then converting.


(Ben Cohen) #2

I would say generally no. These are all trivially compassable with a combination of RRC's init + lazy equivalents:

let seq = 0..<10
let ca = ContiguousArray(seq.lazy.map(String.init))

(Adrian Zubarev) #3

What @Ben_Cohen said, however I'm keen to see iff we get higher kinded types and some other generic features if those functions might switch to a different representation.

The following syntax is not valid Swift syntax, it's completely hypothetical and borrows a few forms that were floating around here on the forums:

protocol Sequence {
  associatedtype Element

  func map<Result>(
    _ closure: (Self.Element) throws -> Result.Element /* = { $0 } */
  ) rethrows -> Result where Result ~= Self

  func compactMap<Result>(
    _ closure: (Self.Element) throws -> Result.Element?
  ) rethrows -> Result where Result ~= Self

  func flatMap<T, Result>(
    _ closure: (Self.Element) throws -> T
  ) rethrows -> Result 
    where 
    T : Sequence,
    Result ~= Self, 
    Result.Element == T.Element
}

Result ~= Self differs from Result == Self that is only a Sequence with un-specified constraints such as Element, which allows us to re-assign it in through type inference of the function. The above example also has a commented out part that indicates that for instance map should by default map to Self if no explicit type provided, otherwise it could theoretically map to any other sequence type.