TL; DR langsung ke contoh terakhir
Saya akan mencoba dan merekap.
Definisi
The forpemahaman adalah cara pintas sintaks untuk menggabungkan flatMapdan mapdengan cara yang mudah dibaca dan alasan tentang.
Mari kita sederhanakan sedikit dan asumsikan bahwa setiap classyang menyediakan kedua metode yang disebutkan di atas dapat disebut a monaddan kita akan menggunakan simbol yang M[A]berarti a monaddengan tipe bagian dalam A.
Contoh
Beberapa monad yang biasa terlihat meliputi:
List[String] dimana
M[X] = List[X]
A = String
Option[Int] dimana
Future[String => Boolean] dimana
M[X] = Future[X]
A = (String => Boolean)
map dan flatMap
Didefinisikan dalam monad generik M[A]
def map(f: A => B): M[B]
def flatMap(f: A => M[B]): M[B]
misalnya
val list = List("neo", "smith", "trinity")
val f: String => List[Int] = s => s.map(_.toInt).toList
list map f
>> List(List(110, 101, 111), List(115, 109, 105, 116, 104), List(116, 114, 105, 110, 105, 116, 121))
list flatMap f
>> List(110, 101, 111, 115, 109, 105, 116, 104, 116, 114, 105, 110, 105, 116, 121)
untuk ekspresi
Setiap baris dalam ekspresi yang menggunakan <-simbol diterjemahkan menjadi flatMappanggilan, kecuali baris terakhir yang diterjemahkan menjadi mappanggilan penutup , di mana "simbol terikat" di sisi kiri diteruskan sebagai parameter ke fungsi argumen (apa kami sebelumnya menelepon f: A => M[B]):
for {
bound <- list
out <- f(bound)
} yield out
list.flatMap { bound =>
f(bound).map { out =>
out
}
}
list.flatMap { bound =>
f(bound)
}
list flatMap f
Sebuah ekspresi-untuk dengan hanya satu <-akan diubah menjadi mappanggilan dengan ekspresi yang diteruskan sebagai argumen:
for {
bound <- list
} yield f(bound)
list.map { bound =>
f(bound)
}
list map f
Sekarang langsung ke intinya
Seperti yang Anda lihat, mapoperasi tersebut mempertahankan "bentuk" aslinya monad, sehingga hal yang sama terjadi untuk yieldekspresi: a Listtetap a Listdengan konten yang diubah oleh operasi di yield.
Di sisi lain, setiap garis pengikat di dalam forhanyalah komposisi yang berurutanmonads , yang harus "diratakan" untuk mempertahankan satu "bentuk luar".
Misalkan sejenak bahwa setiap pengikatan internal diterjemahkan menjadi mappanggilan, tetapi tangan kanan memiliki A => M[B]fungsi yang sama , Anda akan berakhir dengan a M[M[B]]untuk setiap baris dalam pemahaman.
Maksud dari keseluruhan forsintaks adalah untuk dengan mudah "meratakan" rangkaian operasi monadik yang berurutan (yaitu, operasi yang "mengangkat" nilai dalam "bentuk monadik":) A => M[B], dengan penambahan mapoperasi akhir yang mungkin melakukan transformasi penutup.
Saya harap ini menjelaskan logika di balik pilihan terjemahan, yang diterapkan secara mekanis, yaitu: n flatMappanggilan bersarang yang diakhiri dengan satu mappanggilan.
Contoh ilustratif yang dibuat-buat.
Dimaksudkan untuk menunjukkan ekspresi forsintaksis
case class Customer(value: Int)
case class Consultant(portfolio: List[Customer])
case class Branch(consultants: List[Consultant])
case class Company(branches: List[Branch])
def getCompanyValue(company: Company): Int = {
val valuesList = for {
branch <- company.branches
consultant <- branch.consultants
customer <- consultant.portfolio
} yield (customer.value)
valuesList reduce (_ + _)
}
Bisakah Anda menebak jenisnya valuesList ?
Seperti yang sudah dikatakan, bentuk dari yang monaddipertahankan melalui pemahaman, jadi kita mulai dengan Listdalam company.branches, dan harus diakhiri dengan a List.
Tipe bagian dalam berubah dan ditentukan oleh yieldekspresi: yaitucustomer.value: Int
valueList harus menjadi List[Int]