Reconsidering the (Element -> T?) variant of SequenceType.flatMap


(Al Skipp) #1

It’s ‘flatMap’, Jim, but not as we know it!

I’ve also noticed this override of flatMap has caused confusion. It’s not uncommon for people to see flatMap, think, what’s that then? One internet search later and they’re wiggling around in a can of monadic worms, but with the extra burden that this particular override doesn’t have the type signature of the ‘official’ flatMap. Compounding the confusion about monads seems like a rather cruel trick to play on the unwary. The function is certainly very useful, if only it had a different name.

Al

···

Hello, all! This SequenceType-implemented flatMap recently caused some confusion on my team:

func flatMap<T>(@noescape transform: (Self.Generator.Element) throws -> T?) rethrows -> [T]

I’m a big fan of this operator in various functional libraries, but I admit I was a bit surprised to see the “flatMap” terminology appear in the Swift stdlib in the first place—its naming is certainly a notch obscure!

From the reactions of teammates in code reviews involving these methods, there was a significant difference in comprehensibility between the Element -> [T] variant and the Element -> T? variant. The former was easily explained by “it’s a map, followed by a flatten,” whereas the same explanation failed in the latter case.

I expect that the inspiration came from Scala, where the equivalent definition has a transformer essentially of type Element -> GeneratorType<T>; separately, their optionals are implicitly convertible to (their equivalent of) GeneratorType. So, in the end, in Scala, you can effectively flatMap with an Element -> T? transformer.

But Optional doesn’t implement GeneratorType, and I’d (weakly) argue that it shouldn’t. And if we think about flatMap in the context of a monadic bind (I do, anyway!), it’s especially surprising that the transformer is operating in a different monadic context (Optional) than the receiver (SequenceType). Unless we made Optional adopt SequenceType, in which case we could consider the bind to be happening in that context.

In conclusion, I argue that this overload is confusing both to folks unfamiliar with FP (because it doesn’t feel like Optionals can be flattened) and to folks familiar with FP (because it implies binding across monadic contexts).