TL; DR langsung ke contoh terakhir
Saya akan mencoba dan merekap.
Definisi
The for
pemahaman adalah cara pintas sintaks untuk menggabungkan flatMap
dan map
dengan cara yang mudah dibaca dan alasan tentang.
Mari kita sederhanakan sedikit dan asumsikan bahwa setiap class
yang menyediakan kedua metode yang disebutkan di atas dapat disebut a monad
dan kita akan menggunakan simbol yang M[A]
berarti a monad
dengan 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 flatMap
panggilan, kecuali baris terakhir yang diterjemahkan menjadi map
panggilan 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 map
panggilan 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, map
operasi tersebut mempertahankan "bentuk" aslinya monad
, sehingga hal yang sama terjadi untuk yield
ekspresi: a List
tetap a List
dengan konten yang diubah oleh operasi di yield
.
Di sisi lain, setiap garis pengikat di dalam for
hanyalah komposisi yang berurutanmonads
, yang harus "diratakan" untuk mempertahankan satu "bentuk luar".
Misalkan sejenak bahwa setiap pengikatan internal diterjemahkan menjadi map
panggilan, 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 for
sintaks adalah untuk dengan mudah "meratakan" rangkaian operasi monadik yang berurutan (yaitu, operasi yang "mengangkat" nilai dalam "bentuk monadik":) A => M[B]
, dengan penambahan map
operasi akhir yang mungkin melakukan transformasi penutup.
Saya harap ini menjelaskan logika di balik pilihan terjemahan, yang diterapkan secara mekanis, yaitu: n
flatMap
panggilan bersarang yang diakhiri dengan satu map
panggilan.
Contoh ilustratif yang dibuat-buat.
Dimaksudkan untuk menunjukkan ekspresi for
sintaksis
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 monad
dipertahankan melalui pemahaman, jadi kita mulai dengan List
dalam company.branches
, dan harus diakhiri dengan a List
.
Tipe bagian dalam berubah dan ditentukan oleh yield
ekspresi: yaitucustomer.value: Int
valueList
harus menjadi List[Int]