SE-0187: Introduce Sequence.filterMap(_:)

I accept the motivation for this change, although I would mention that:
- in 2.5 years on a ~200k lines of Swift project
- we've seen a plenty of instances of `flatMap` used where `map` would have
been sufficient, but
- we've never burned time on tracking down the sort of compiler issue
described by the author in the Motivation. As I say, the argument is fair,
but I am questioning how frequently this problem occurs.

I can't speak to the previous attempts to solve this problem, but I'd add
my voice to anyone advocating solving this by having a compiler warning
when `flatMap` is used redundantly(?).

If this proposal were to be accepted, I'd also question the choice of
`filterMap` as a name. This name is presumably shorthand for
`filterNonesAndMap`, which conveniently ignores the following:
- `flatMap` may be used on a sequence-of-sequences to flatten our to a
single-dimensional sequence.
- `flatMap` may be used on other 'box' types, e.g. on an optional: `Optional
<String>("foo").flatMap { .some($0 + "bar") }` or on the common `Result`
type.

Any re-naming of one `flatMap` should take these other uses into account,
as they share conceptual details.

Thanks,

Sam

This proposal only proposes renaming the “filterNonesAndMap” variant (though that name is misleading, it would be “filterNonesAfterMapping” if anything); the other uses of flatMap will remain unchanged.

-BJ

···

On Nov 9, 2017, at 9:37 AM, Sam Warner via swift-evolution <swift-evolution@swift.org> wrote:

I accept the motivation for this change, although I would mention that:
- in 2.5 years on a ~200k lines of Swift project
- we've seen a plenty of instances of `flatMap` used where `map` would have been sufficient, but
- we've never burned time on tracking down the sort of compiler issue described by the author in the Motivation. As I say, the argument is fair, but I am questioning how frequently this problem occurs.

I can't speak to the previous attempts to solve this problem, but I'd add my voice to anyone advocating solving this by having a compiler warning when `flatMap` is used redundantly(?).

If this proposal were to be accepted, I'd also question the choice of `filterMap` as a name. This name is presumably shorthand for `filterNonesAndMap`, which conveniently ignores the following:
- `flatMap` may be used on a sequence-of-sequences to flatten our to a single-dimensional sequence.
- `flatMap` may be used on other 'box' types, e.g. on an optional: `Optional<String>("foo").flatMap { .some($0 + "bar") }` or on the common `Result` type.

Any re-naming of one `flatMap` should take these other uses into account, as they share conceptual details.

Thanks,

Sam

This proposal only proposes renaming the “filterNonesAndMap” variant (though that name is misleading, it would be “filterNonesAfterMapping” if anything); the other uses of flatMap will remain unchanged.

… then it should be really right: As I learned in the thread, there is a map step before filter, and another map afterwards - so „filterNonesAfterMappingAndMap“ (that’s why I don’t like the use of the term „filter“ here — if you want an accurate name that contains this verb, it gets quite complicated…)

I accept the motivation for this change, although I would mention that:
- in 2.5 years on a ~200k lines of Swift project
- we've seen a plenty of instances of `flatMap` used where `map` would have been sufficient, but
- we've never burned time on tracking down the sort of compiler issue described by the author in the Motivation. As I say, the argument is fair, but I am questioning how frequently this problem occurs.

I can't speak to the previous attempts to solve this problem, but I'd add my voice to anyone advocating solving this by having a compiler warning when `flatMap` is used redundantly(?).

If this proposal were to be accepted, I'd also question the choice of `filterMap` as a name. This name is presumably shorthand for `filterNonesAndMap`, which conveniently ignores the following:
- `flatMap` may be used on a sequence-of-sequences to flatten our to a single-dimensional sequence.
- `flatMap` may be used on other 'box' types, e.g. on an optional: `Optional<String>("foo").flatMap { .some($0 + "bar") }` or on the common `Result` type.

Any re-naming of one `flatMap` should take these other uses into account, as they share conceptual details.

Thanks,

Sam

This proposal only proposes renaming the “filterNonesAndMap” variant (though that name is misleading, it would be “filterNonesAfterMapping” if anything); the other uses of flatMap will remain unchanged.

I wonder, if it's also about unwrapping optionals (not just about filtering nils out; so correct name seems like mapOptionalsThenSkipNilsAndUnwrap), why not unwrapMap, optionalMap or maybe flatOptionalMap or even flatUnwrapMap, or mapUnwrapped? Such name IMO will be more clear about what is the purpose, because personally for me 'filterMap' is very general name, something about filtering and map, nothing about optional/unwrapping/nils. I even think 'flatMap' has a better mental model(we have optionals .some and nils in array, and will have an array of unwrapped(flat) values without nils).

For comparision:

let a : [Int?] = [1,2,3,nil,4,nil,5]

let b = a.flatMap { $0.flatMap{$0*10} } // current

let b = a.filterMap { $0.filterMap {$0*10} } // suggested

let b = a.unwrapMap { $0.unwrapMap {$0*10} } // alternative

let b = a.optionalMap { $0.optionalMap {$0*10} } // alternative

print(b) // [10, 20, 30, 40, 50]

Vladimir.

···

On 09.11.2017 19:41, BJ Homer via swift-evolution wrote:

On Nov 9, 2017, at 9:37 AM, Sam Warner via swift-evolution <swift-evolution@swift.org> wrote:

-BJ
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

This proposal only proposes renaming the “filterNonesAndMap” variant (though that name is misleading, it would be “filterNonesAfterMapping” if anything); the other uses of flatMap will remain unchanged.

… then it should be really right: As I learned in the thread, there is a map step before filter, and another map afterwards - so „filterNonesAfterMappingAndMap“ (that’s why I don’t like the use of the term „filter“ here — if you want an accurate name that contains this verb, it gets quite complicated…)

But obviously “filterNonesAfterMappingAndMap” is not a name we’re going to end up with. You can argue that “filterMap” is not completely faithful to the internal implementation, but I think it’s clearly better than “flatMap” in this case, which is even more incorrect. I’m not opposed to finding a better name than “filterMap”, but so far I haven’t seen one.

-BJ

···

On Nov 9, 2017, at 11:35 AM, Tino Heth <2th@gmx.de> wrote:

At the risk of taking us further down the rabbit hole…

You really want:

  let b = a.flatMap { $0.map{$0*10} } // current

here. That is, a’s Element is Int?, and you want to apply Int->Int ($0*10) within the Int?, which is done with Optional.map<U>(_:(Wrapped)->U)->U?

Optional.flatMap<U>(_:(Wrapped)->U?) -> U? is for when you want to apply a function that returns an optional, but want to avoid “double-wrapping". So for example, you have a [String?], and you want to turn it into [Int] dropping any strings that are either nil or not an integer, you’d write:

  ["1","2",nil].flatMap { $0.flatMap(Int.init) }

Which would return an [Int] of [1,2].

Optional.flatMap has the same “it happens to work with non-optional-returning functions” behavior as Collection.flatMap. But, though strange, it doesn’t cause the same level of active harm as Collection.flatMap. because we don’t implicitly convert elements to collections of elements.

···

On Nov 9, 2017, at 11:43 AM, Vladimir.S via swift-evolution <swift-evolution@swift.org> wrote:

let a : [Int?] = [1,2,3,nil,4,nil,5]

let b = a.flatMap { $0.flatMap{$0*10} } // current

What I meant by flagging the renaming of the generic `flatMap` to the more
specific-to-this-case `filterMap` is that all of the current uses of
`flatMap` share conceptual details: they all process whatever is in the
box, then perform one level of flattening.

This shared concept is hugely valuable, IMO, for people who are new to
Swift in being able to form an understanding of what these functions do,
and also for library authors who are creating box types and want
discoverable, intuitive API names.

This is the reason why I lean towards keeping `flatMap` and adding a
compiler warning for incorrect/redundant uses.

Cheers,

Sam

···

On Fri, 10 Nov 2017 at 05:43 BJ Homer <bjhomer@gmail.com> wrote:

On Nov 9, 2017, at 11:35 AM, Tino Heth <2th@gmx.de> wrote:

This proposal only proposes renaming the “filterNonesAndMap” variant
(though that name is misleading, it would be “filterNonesAfterMapping” if
anything); the other uses of flatMap will remain unchanged.

… then it should be really right: As I learned in the thread, there is a
map step before filter, and another map afterwards - so
„filterNonesAfterMappingAndMap“ (that’s why I don’t like the use of the
term „filter“ here — if you want an accurate name that contains this verb,
it gets quite complicated…)

But obviously “filterNonesAfterMappingAndMap” is not a name we’re going to
end up with. You can argue that “filterMap” is not completely faithful to
the internal implementation, but I think it’s clearly better than “flatMap”
in this case, which is even *more* incorrect. I’m not opposed to finding
a better name than “filterMap”, but so far I haven’t seen one.

-BJ