Hallo Habr!
Mein Name ist Alexander Zimin, ich bin ein iOS-Entwickler bei Badoo. Dies ist eine Übersetzung eines Artikels meines Kollegen Schwib, in dem er beschrieb, wie die flatMap-Funktion in Swift war und warum eine ihrer Überladungen in compactMap umbenannt wurde. Der Artikel ist sowohl zum Verständnis der im 
Swift-Repository und 
seiner Entwicklung ablaufenden Prozesse als auch für die allgemeine Entwicklung hilfreich.

In der funktionalen Programmierung gibt es eine klare Definition der 
flatMap Funktion. Die 
flatMap Methode verwendet eine Liste und eine Transformationsfunktion (die für jede Transformation null oder mehr Werte erwartet), wendet sie auf jedes Element der Liste an und erstellt eine einzelne (abgeflachte) Liste. Dieses Verhalten unterscheidet sich von der einfachen 
map , die eine Transformation auf jeden Wert anwendet und erwartet, dass für jede Transformation nur ein Wert erhalten wird.

Für mehrere Versionen verfügt Swift über eine 
map und eine 
flatMap . In 
Swift 4.1 können Sie 
flatMap jedoch nicht mehr auf eine Folge von Werten anwenden und dennoch einen Abschluss übergeben, der einen optionalen Wert zurückgibt. 
compactMap gibt es jetzt eine 
compactMap Methode.
Zunächst ist es möglicherweise nicht so einfach, das Wesentliche der Innovation zu verstehen. Wenn 
flatMap gut funktioniert hat, warum eine separate Methode einführen? Lass es uns herausfinden.
Die Swift-Standardbibliothek vor Version 4.1 bot drei Implementierungen von Überladungen für 
flatMap :
 1. Sequence.flatMap<S>(_: (Element) -> S) -> [S.Element],  S : Sequence 2. Optional.flatMap<U>(_: (Wrapped) -> U?) -> U? 3. Sequence.flatMap<U>(_: (Element) -> U?) -> [U] 
Lassen Sie uns alle drei Optionen durchgehen und sehen, was sie tun.
Sequence.flatMap <S> (_: (Element) -> S) -> [S.Element], wobei S: Sequence
Die erste Überladung betrifft Sequenzen, bei denen ein Abschluss ein Element dieser Sequenz nimmt und in eine andere Sequenz konvertiert.
flatMap alle diese transformierten Sequenzen in die endgültige Sequenz, die als Ergebnis zurückgegeben wird. Zum Beispiel:
 let array = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] let flattened = array.flatMap { $0 }  
Dies ist ein großartiges Beispiel dafür, wie die 
flatMap Methode funktionieren sollte. Wir werden jedes Element der Quellliste transformieren (zuordnen) und eine neue Sequenz erstellen. Dank 
flatMap Endergebnis eine abgeflachte Struktur transformierter Sequenzen.
Optional.flatMap <U> (_: (Wrapped) -> U?) -> U?
Die zweite Überlastung betrifft optionale Typen. Wenn der aufgerufene optionale Typ einen Wert hat, wird der Abschluss mit dem Wert ohne den optionalen Wrapper (nicht umschlossener Wert) aufgerufen, und Sie können den konvertierten optionalen Wert zurückgeben.
 let a: Int? = 2 let transformedA = a.flatMap { $0 * 2 }  
Sequence.flatMap <U> (_: (Element) -> U?) -> [U]
Die dritte Überlastung hilft Ihnen zu verstehen, wofür 
compactMap . Diese Version sieht genauso aus wie die erste, aber es gibt einen wichtigen Unterschied. In diesem Fall wird der Verschluss optional zurückgegeben. 
flatMap verarbeitet es, überspringt die zurückgegebenen 
flatMap und schließt den Rest ein - im Ergebnis als Werte ohne Wrapper.
 let array = [1, 2, 3, 4, nil, 5, 6, nil, 7] let arrayWithoutNils = array.flatMap { $0 }  
In diesem Fall wird die Bestellung jedoch nicht ausgeführt. Daher ist diese Version von 
flatMap näher an der 
map als die rein funktionale Definition von 
flatMap . Und das Problem mit dieser Überlastung ist, dass Sie sie möglicherweise nicht richtig verwenden, wenn die 
map perfekt funktionieren würde.
 let array = [1, 2, 3, 4, 5, 6] let transformed = array.flatMap { $0 }  
Diese Verwendung von 
flatMap entspricht der dritten Überladung, bei der der konvertierte Wert implizit in optional eingeschlossen und dann der Wrapper entfernt wird, um das Ergebnis zu ergänzen. Die Situation wird besonders interessant, wenn String-Konvertierungen nicht korrekt verwendet werden.
 struct Person { let name: String } let people = [Person(name: “Foo”), Person(name: “Bar”)] let names = array.flatMap { $0.name } 
In Swift vor Version 4.0 würden wir eine Konvertierung in 
[“Foo”, “Bar”] . Ab Version 4.0 implementieren Zeichenfolgenwerte jedoch das Collection-Protokoll. Daher entspricht unsere Verwendung von 
flatMap in diesem Fall anstelle der dritten Überladung der ersten, und wir erhalten ein "abgeflachtes" Ergebnis aus den konvertierten Werten: 
[“F”, “o”, “o”, “B”, “a”, “r”]Beim Aufruf von 
flatMap wird keine Fehlermeldung 
flatMap da dies zulässig ist. Die Logik wird jedoch unterbrochen, da das Ergebnis vom Typ 
Array<Character>.Type und nicht vom erwarteten 
Array<String>.Type .
Fazit
Um den Missbrauch von 
flatMap zu vermeiden, wurde die dritte überladene Version aus der neuen Swift-Version entfernt. Und um das gleiche Problem zu lösen ( 
compactMap entfernen), müssen Sie jetzt eine separate Methode verwenden - 
compactMap .