Skip to content

Commit

Permalink
[stdlib] Introduce compactMap and deprecate one flatMap variant (#13807)
Browse files Browse the repository at this point in the history
This is the implementation of
[SE-0187](https://github.com/apple/swift-evolution/blob/master/proposals/0187-introduce-filtermap.md)

<rdar://problem/34918180>

Cherrypicked and squashed from the following commits:
6f7aecd fb49b3e 8ec6c45 96d3439 128092a
  • Loading branch information
moiseev authored and airspeedswift committed Jan 9, 2018
1 parent afc8bad commit c167720
Show file tree
Hide file tree
Showing 10 changed files with 134 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ internal func readUInt() -> UInt {
/// process.
internal func sendReflectionInfos() {
debugLog("BEGIN \(#function)"); defer { debugLog("END \(#function)") }
let infos = (0..<_dyld_image_count()).flatMap(getReflectionInfoForImage)
let infos = (0..<_dyld_image_count()).compactMap(getReflectionInfoForImage)

var numInfos = infos.count
debugLog("\(numInfos) reflection info bundles.")
Expand Down
2 changes: 1 addition & 1 deletion stdlib/public/SDK/Foundation/JSONEncoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1212,7 +1212,7 @@ fileprivate struct _JSONKeyedDecodingContainer<K : CodingKey> : KeyedDecodingCon
// MARK: - KeyedDecodingContainerProtocol Methods

public var allKeys: [Key] {
return self.container.keys.flatMap { Key(stringValue: $0) }
return self.container.keys.compactMap { Key(stringValue: $0) }
}

public func contains(_ key: Key) -> Bool {
Expand Down
2 changes: 1 addition & 1 deletion stdlib/public/SDK/Foundation/PlistEncoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -779,7 +779,7 @@ fileprivate struct _PlistKeyedDecodingContainer<K : CodingKey> : KeyedDecodingCo
// MARK: - KeyedDecodingContainerProtocol Methods

public var allKeys: [Key] {
return self.container.keys.flatMap { Key(stringValue: $0) }
return self.container.keys.compactMap { Key(stringValue: $0) }
}

public func contains(_ key: Key) -> Bool {
Expand Down
50 changes: 47 additions & 3 deletions stdlib/public/core/FlatMap.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ extension LazySequenceProtocol {
FlattenSequence<LazyMapSequence<Elements, SegmentOfResult>>> {
return self.map(transform).joined()
}

/// Returns the non-`nil` results of mapping the given transformation over
/// this sequence.
///
Expand All @@ -39,7 +39,7 @@ extension LazySequenceProtocol {
///
/// - Complexity: O(1)
@_inlineable // FIXME(sil-serialize-all)
public func flatMap<ElementOfResult>(
public func compactMap<ElementOfResult>(
_ transform: @escaping (Elements.Element) -> ElementOfResult?
) -> LazyMapSequence<
LazyFilterSequence<
Expand All @@ -48,6 +48,28 @@ extension LazySequenceProtocol {
> {
return self.map(transform).filter { $0 != nil }.map { $0! }
}

/// Returns the non-`nil` results of mapping the given transformation over
/// this sequence.
///
/// Use this method to receive a sequence of nonoptional values when your
/// transformation produces an optional value.
///
/// - Parameter transform: A closure that accepts an element of this sequence
/// as its argument and returns an optional value.
///
/// - Complexity: O(1)
@inline(__always)
@available(*, deprecated, renamed: "compactMap(_:)")
public func flatMap<ElementOfResult>(
_ transform: @escaping (Elements.Element) -> ElementOfResult?
) -> LazyMapSequence<
LazyFilterSequence<
LazyMapSequence<Elements, ElementOfResult?>>,
ElementOfResult
> {
return self.compactMap(transform)
}
}

extension LazyCollectionProtocol {
Expand All @@ -69,7 +91,28 @@ extension LazyCollectionProtocol {
> {
return self.map(transform).joined()
}


/// Returns the non-`nil` results of mapping the given transformation over
/// this collection.
///
/// Use this method to receive a collection of nonoptional values when your
/// transformation produces an optional value.
///
/// - Parameter transform: A closure that accepts an element of this
/// collection as its argument and returns an optional value.
///
/// - Complexity: O(1)
@_inlineable // FIXME(sil-serialize-all)
public func compactMap<ElementOfResult>(
_ transform: @escaping (Elements.Element) -> ElementOfResult?
) -> LazyMapCollection<
LazyFilterCollection<
LazyMapCollection<Elements, ElementOfResult?>>,
ElementOfResult
> {
return self.map(transform).filter { $0 != nil }.map { $0! }
}

/// Returns the non-`nil` results of mapping the given transformation over
/// this collection.
///
Expand All @@ -80,6 +123,7 @@ extension LazyCollectionProtocol {
/// collection as its argument and returns an optional value.
///
/// - Complexity: O(1)
@available(*, deprecated, renamed: "compactMap(_:)")
@_inlineable // FIXME(sil-serialize-all)
public func flatMap<ElementOfResult>(
_ transform: @escaping (Elements.Element) -> ElementOfResult?
Expand Down
38 changes: 35 additions & 3 deletions stdlib/public/core/SequenceAlgorithms.swift.gyb
Original file line number Diff line number Diff line change
Expand Up @@ -727,6 +727,37 @@ extension Sequence {
}

extension Sequence {
/// Returns an array containing the non-`nil` results of calling the given
/// transformation with each element of this sequence.
///
/// Use this method to receive an array of nonoptional values when your
/// transformation produces an optional value.
///
/// In this example, note the difference in the result of using `map` and
/// `compactMap` with a transformation that returns an optional `Int` value.
///
/// let possibleNumbers = ["1", "2", "three", "///4///", "5"]
///
/// let mapped: [Int?] = possibleNumbers.map { str in Int(str) }
/// // [1, 2, nil, nil, 5]
///
/// let flatMapped: [Int] = possibleNumbers.compactMap { str in Int(str) }
/// // [1, 2, 5]
///
/// - Parameter transform: A closure that accepts an element of this
/// sequence as its argument and returns an optional value.
/// - Returns: An array of the non-`nil` results of calling `transform`
/// with each element of the sequence.
///
/// - Complexity: O(*m* + *n*), where *m* is the length of this sequence
/// and *n* is the length of the result.
@_inlineable
public func compactMap<ElementOfResult>(
_ transform: (Element) throws -> ElementOfResult?
) rethrows -> [ElementOfResult] {
return try _compactMap(transform)
}

/// Returns an array containing the non-`nil` results of calling the given
/// transformation with each element of this sequence.
///
Expand All @@ -751,19 +782,20 @@ extension Sequence {
///
/// - Complexity: O(*m* + *n*), where *m* is the length of this sequence
/// and *n* is the length of the result.
@_inlineable
@inline(__always)
@available(*, deprecated, renamed: "compactMap(_:)")
public func flatMap<ElementOfResult>(
_ transform: (Element) throws -> ElementOfResult?
) rethrows -> [ElementOfResult] {
return try _flatMap(transform)
return try _compactMap(transform)
}

// The implementation of flatMap accepting a closure with an optional result.
// Factored out into a separate functions in order to be used in multiple
// overloads.
@_inlineable // FIXME(sil-serialize-all)
@inline(__always)
public func _flatMap<ElementOfResult>(
public func _compactMap<ElementOfResult>(
_ transform: (Element) throws -> ElementOfResult?
) rethrows -> [ElementOfResult] {
var result: [ElementOfResult] = []
Expand Down
10 changes: 9 additions & 1 deletion stdlib/public/core/StringRangeReplaceableCollection.swift.gyb
Original file line number Diff line number Diff line change
Expand Up @@ -442,10 +442,18 @@ extension Sequence {

extension Collection {
@_inlineable // FIXME(sil-serialize-all)
public func compactMap(
_ transform: (Element) throws -> String?
) rethrows -> [String] {
return try _compactMap(transform)
}

@available(*, deprecated, renamed: "compactMap(_:)")
@inline(__always)
public func flatMap(
_ transform: (Element) throws -> String?
) rethrows -> [String] {
return try _flatMap(transform)
return try _compactMap(transform)
}
}
//===----------------------------------------------------------------------===//
Expand Down
2 changes: 1 addition & 1 deletion test/Constraints/casts.swift
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ _ = b1 as Int // expected-error {{cannot convert value of type 'Bool' to type
_ = seven as Int // expected-error {{cannot convert value of type 'Double' to type 'Int' in coercion}}

func rdar29894174(v: B?) {
let _ = [v].flatMap { $0 as? D }
let _ = [v].compactMap { $0 as? D }
}

// When re-typechecking a solution with an 'is' cast applied,
Expand Down
6 changes: 3 additions & 3 deletions test/Constraints/closures.swift
Original file line number Diff line number Diff line change
Expand Up @@ -511,12 +511,12 @@ let _: ((Int?) -> Void) = { (arg: Int!) in }
// () -> T to () -> Optional<()>.
func returnsArray() -> [Int] { return [] }

returnsArray().flatMap { $0 }.flatMap { }
returnsArray().compactMap { $0 }.compactMap { }
// expected-warning@-1 {{expression of type 'Int' is unused}}
// expected-warning@-2 {{result of call to 'flatMap' is unused}}
// expected-warning@-2 {{result of call to 'compactMap' is unused}}

// rdar://problem/30271695
_ = ["hi"].flatMap { $0.isEmpty ? nil : $0 }
_ = ["hi"].compactMap { $0.isEmpty ? nil : $0 }

// rdar://problem/32432145 - compiler should emit fixit to remove "_ in" in closures if 0 parameters is expected

Expand Down
8 changes: 4 additions & 4 deletions test/Constraints/tuple_arguments.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1554,11 +1554,11 @@ extension Sequence where Iterator.Element == (key: String, value: String?) {
}

func rdar33043106(_ records: [(Int)], _ other: [((Int))]) -> [Int] {
let x: [Int] = records.flatMap { _ in
let x: [Int] = records.map { _ in
let i = 1
return i
}
let y: [Int] = other.flatMap { _ in
let y: [Int] = other.map { _ in
let i = 1
return i
}
Expand All @@ -1571,9 +1571,9 @@ func itsFalse(_: Int) -> Bool? {
}

func rdar33159366(s: AnySequence<Int>) {
_ = s.flatMap(itsFalse)
_ = s.compactMap(itsFalse)
let a = Array(s)
_ = a.flatMap(itsFalse)
_ = a.compactMap(itsFalse)
}

func sr5429<T>(t: T) {
Expand Down
32 changes: 32 additions & 0 deletions test/stdlib/FlatMapDeprecation.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// RUN: %target-typecheck-verify-swift %s

func flatMapOnSequence<
S : Sequence
>(xs: S, f: (S.Element) -> S.Element?) {
_ = xs.flatMap(f) // expected-warning {{deprecated}} expected-note {{compactMap}}
}

func flatMapOnLazySequence<
S : LazySequenceProtocol
>(xs: S, f: (S.Element) -> S.Element?) {
_ = xs.flatMap(f) // expected-warning {{deprecated}} expected-note {{compactMap}}
}

func flatMapOnLazyCollection<
C : LazyCollectionProtocol
>(xs: C, f: (C.Element) -> C.Element?) {
_ = xs.flatMap(f) // expected-warning {{deprecated}} expected-note {{compactMap}}
}

func flatMapOnLazyBidirectionalCollection<
C : LazyCollectionProtocol & BidirectionalCollection
>(xs: C, f: (C.Element) -> C.Element?)
where C.Elements : BidirectionalCollection {
_ = xs.flatMap(f) // expected-warning {{deprecated}} expected-note {{compactMap}}
}

func flatMapOnCollectinoOfStrings<
C : Collection
>(xs: C, f: (C.Element) -> String?) {
_ = xs.flatMap(f) // expected-warning {{deprecated}} expected-note {{compactMap}}
}

0 comments on commit c167720

Please sign in to comment.