Swift 4.1: mengapa Apple mengganti nama flatMap menjadi compactMap

Halo, Habr!

Nama saya Alexander Zimin, saya adalah pengembang iOS di Badoo. Ini adalah terjemahan dari artikel oleh kolega saya, Schwib, di mana dia menggambarkan seperti apa fungsi flatMap di Swift dan mengapa salah satu kelebihannya dinamai compactMap. Artikel ini bermanfaat baik untuk memahami proses yang terjadi di repositori Swift dan evolusinya , serta untuk pengembangan umum.



Dalam pemrograman fungsional, ada definisi yang jelas tentang apa fungsi flatMap . Metode flatMap mengambil daftar dan fungsi transformasi (yang untuk setiap transformasi mengharapkan untuk mendapatkan nilai nol atau lebih), menerapkannya ke setiap elemen daftar dan membuat daftar tunggal (diratakan). Perilaku ini berbeda dari fungsi map sederhana, yang menerapkan transformasi untuk setiap nilai dan berharap untuk mendapatkan hanya satu nilai untuk setiap transformasi.



Untuk beberapa versi, Swift memiliki map dan flatMap . Namun, di Swift 4.1 Anda tidak bisa lagi menerapkan flatMap ke urutan nilai dan masih melewati penutupan yang mengembalikan nilai opsional. Sekarang ada metode compactMap untuk compactMap .

Pada awalnya mungkin tidak begitu mudah untuk memahami esensi dari inovasi. Jika flatMap bekerja dengan baik, mengapa memperkenalkan metode terpisah? Mari kita cari tahu.

Perpustakaan standar Swift sebelum versi 4.1 memberikan tiga implementasi kelebihan beban untuk 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] 

Mari kita pergi melalui ketiga opsi dan melihat apa yang mereka lakukan.

Sequence.flatMap <S> (_: (Elemen) -> S) -> [S.Element], di mana S: Sequence


Kelebihan pertama adalah untuk urutan di mana penutupan mengambil elemen dari urutan itu dan mengubah ke urutan lain.
flatMap - flatMap semua urutan yang diubah ini ke dalam urutan akhir yang dikembalikan sebagai hasilnya. Sebagai contoh:

 let array = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] let flattened = array.flatMap { $0 } // [1, 2, 3, 4, 5, 6, 7, 8, 9] 

Ini adalah contoh yang bagus tentang bagaimana metode flatMap seharusnya bekerja. Kami akan mengubah (memetakan) setiap elemen dari daftar sumber dan membuat urutan baru. Berkat flatMap hasil akhirnya adalah struktur rata dari sekuens yang ditransformasikan.

Opsional.flatMap <U> (_: (Dibungkus) -> U?) -> U?


Kelebihan kedua adalah untuk tipe opsional. Jika jenis opsional yang Anda panggil memiliki nilai, maka penutupan akan dipanggil dengan nilai tanpa pembungkus opsional (nilai yang tidak dibuka), dan Anda dapat mengembalikan nilai opsional yang dikonversi.

 let a: Int? = 2 let transformedA = a.flatMap { $0 * 2 } // 4 let b: Int? = nil let transformedB = b.flatMap { $0 * 2 } // nil 

Sequence.flatMap <U> (_: (Elemen) -> U?) -> [U]


Kelebihan ketiga akan membantu Anda memahami apa compactMap . Versi ini terlihat sama dengan yang pertama, tetapi ada perbedaan penting. Dalam hal ini, penutupan mengembalikan opsional. flatMap memprosesnya, melewatkan nilai-nilai nil yang dikembalikan, dan memasukkan semua sisanya - dalam hasilnya sebagai nilai tanpa pembungkus.

 let array = [1, 2, 3, 4, nil, 5, 6, nil, 7] let arrayWithoutNils = array.flatMap { $0 } // [1, 2, 3, 4, 5, 6, 7] 

Namun dalam kasus ini, pemesanan tidak dilakukan. Oleh karena itu, versi flatMap lebih dekat dengan map daripada definisi fungsional flatMap . Dan masalah dengan kelebihan ini adalah bahwa Anda mungkin tidak menggunakannya dengan benar di mana map akan bekerja dengan sempurna.

 let array = [1, 2, 3, 4, 5, 6] let transformed = array.flatMap { $0 } // same as array.map { $0 } 

Penggunaan flatMap berhubungan dengan kelebihan ketiga, secara implisit membungkus nilai yang dikonversi dalam opsional, dan kemudian menghapus pembungkus untuk menambah hasilnya. Situasi menjadi sangat menarik jika konversi string tidak digunakan dengan benar.

 struct Person { let name: String } let people = [Person(name: β€œFoo”), Person(name: β€œBar”)] let names = array.flatMap { $0.name } 

Di Swift sebelum versi 4.0, kita akan mendapatkan konversi ke [β€œFoo”, β€œBar”] . Tetapi dimulai dengan versi 4.0, nilai string mengimplementasikan protokol Collection. Oleh karena itu, penggunaan flatMap dalam kasus ini, alih-alih beban ketiga, akan sesuai dengan yang pertama, dan kami akan mendapatkan hasil "yang diratakan" dari nilai yang dikonversi: [β€œF”, β€œo”, β€œo”, β€œB”, β€œa”, β€œr”]

Saat memanggil flatMap Anda tidak akan mendapatkan kesalahan, karena ini diperbolehkan digunakan. Tetapi logikanya akan rusak, karena hasilnya adalah tipe Array<Character>.Type , bukan Array<String>.Type diharapkan Array<String>.Type .

Kesimpulan


Untuk menghindari penyalahgunaan flatMap , versi kelebihan beban ketiga telah dihapus dari versi Swift baru. Dan untuk mengatasi masalah yang sama (menghapus nil-nilai) sekarang Anda perlu menggunakan metode terpisah - compactMap .

Source: https://habr.com/ru/post/id414809/


All Articles