TL; DR перейти безпосередньо до останнього прикладу
Я спробую підбити підсумок.
Визначення
forРозуміння є синтаксис ярлика , щоб об'єднати flatMapі mapтаким чином , який легко читати і міркувати о.
Давайте трохи спростимо ситуацію і припустимо, що кожен, classщо забезпечує обидва вищезазначені методи, може бути названий a, monadі ми будемо використовувати символ M[A]для позначення a monadз внутрішнім типом A.
Приклади
Деякі часто зустрічаються монади включають:
List[String] де
M[X] = List[X]
A = String
Option[Int] де
Future[String => Boolean] де
M[X] = Future[X]
A = (String => Boolean)
map and flatMap
Визначається у загальній монаді M[A]
def map(f: A => B): M[B]
def flatMap(f: A => M[B]): M[B]
напр
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)
для виразу
Кожен рядок у виразі, що використовує <-символ, перекладається у flatMapвиклик, за винятком останнього рядка, який перекладається в завершальний mapвиклик, де "зв'язаний символ" зліва передається як параметр функції аргументу (що ми раніше називали 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
Вираз for-only з одним <-перетворюється у mapвиклик із виразом, переданим як аргумент:
for {
bound <- list
} yield f(bound)
list.map { bound =>
f(bound)
}
list map f
Тепер до суті
Як бачите, mapоперація зберігає "форму" оригіналу monad, тому те саме відбувається з yieldвиразом: a Listзалишається a Listзі змістом, перетвореним операцією в yield.
З іншого боку, кожна зв'язувальна лінія в forє просто послідовною композицією monads, яку потрібно "сплющити", щоб зберегти єдину "зовнішню форму".
Припустимо на мить, що кожна внутрішня прив'язка була переведена на mapвиклик, але права рука була тією самою A => M[B]функцією, ви отримаєте M[M[B]]для кожного рядка в розумінні.
Метою цілого forсинтаксису є легке "згладжування" об'єднання послідовних монадичних операцій (тобто операцій, що "піднімають" значення у "монадичній формі":) A => M[B], з додаванням заключної mapоперації, яка, можливо, виконує завершальну трансформацію.
Сподіваюся, це пояснює логіку вибору перекладу, який застосовується механічним способом, тобто: n flatMapвкладені виклики, укладені одним mapвикликом.
Надуманий ілюстративний приклад,
призначений для демонстрації виразності forсинтаксису
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 (_ + _)
}
Чи можете ви вгадати тип valuesList?
Як вже було сказано, форма а monadпідтримується через розуміння, тому ми починаємо з " Listв" company.branchesі повинні закінчуватися "а" List.
Внутрішній тип замість цього змінюється і визначається yieldвиразом: який єcustomer.value: Int
valueList має бути a List[Int]