TL; DR gehen direkt zum letzten Beispiel
Ich werde versuchen, es noch einmal zusammenzufassen.
Definitionen
Das forVerständnis ist eine Syntax Verknüpfung zu kombinieren flatMapund mapin einer Weise , die über leicht zu lesen und Grund.
Vereinfachen wir die Dinge ein wenig und nehmen an, dass jede class, die beide oben genannten Methoden bereitstellt, als a bezeichnet werden kann, monadund wir verwenden das Symbol M[A], um a monadmit einem inneren Typ zu bezeichnen A.
Beispiele
Einige häufig gesehene Monaden sind:
List[String] wo
M[X] = List[X]
A = String
Option[Int] wo
Future[String => Boolean] wo
M[X] = Future[X]
A = (String => Boolean)
map und flatMap
In einer generischen Monade definiert M[A]
/* applies a transformation of the monad "content" mantaining the
* monad "external shape"
* i.e. a List remains a List and an Option remains an Option
* but the inner type changes
*/
def map(f: A => B): M[B]
/* applies a transformation of the monad "content" by composing
* this monad with an operation resulting in another monad instance
* of the same type
*/
def flatMap(f: A => M[B]): M[B]
z.B
val list = List("neo", "smith", "trinity")
//converts each character of the string to its corresponding code
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)
zum Ausdruck bringen
Jede Zeile im Ausdruck, die das <-Symbol verwendet, wird in einen flatMapAufruf übersetzt, mit Ausnahme der letzten Zeile, die in einen abschließenden mapAufruf übersetzt wird, in der das "gebundene Symbol" auf der linken Seite als Parameter an die Argumentfunktion übergeben wird (was wir haben vorher angerufen f: A => M[B]):
// The following ...
for {
bound <- list
out <- f(bound)
} yield out
// ... is translated by the Scala compiler as ...
list.flatMap { bound =>
f(bound).map { out =>
out
}
}
// ... which can be simplified as ...
list.flatMap { bound =>
f(bound)
}
// ... which is just another way of writing:
list flatMap f
Ein for-Ausdruck mit nur einem <-wird in einen mapAufruf mit dem als Argument übergebenen Ausdruck konvertiert :
// The following ...
for {
bound <- list
} yield f(bound)
// ... is translated by the Scala compiler as ...
list.map { bound =>
f(bound)
}
// ... which is just another way of writing:
list map f
Nun zum Punkt
Wie Sie sehen können, behält die mapOperation die "Form" des Originals bei monad, so dass dies auch für den yieldAusdruck gilt: a Listbleibt a Listmit dem durch die Operation in der Datei transformierten Inhalt yield.
Andererseits ist jede Bindungslinie in der fornur eine Zusammensetzung von aufeinanderfolgenden monads, die "abgeflacht" werden muss, um eine einzelne "äußere Form" beizubehalten.
Angenommen, für einen Moment wurde jede interne Bindung in einen mapAufruf übersetzt, aber die rechte Hand war dieselbe A => M[B]Funktion. Sie würden M[M[B]]für jede Zeile im Verständnis eine erhalten.
Die Absicht der gesamten forSyntax besteht darin, die Verkettung aufeinanderfolgender monadischer Operationen (dh Operationen, die einen Wert in einer "monadischen Form" "anheben") leicht zu "reduzieren" A => M[B], wobei eine letzte mapOperation hinzugefügt wird , die möglicherweise eine abschließende Transformation durchführt.
Ich hoffe, dies erklärt die Logik hinter der Wahl der Übersetzung, die auf mechanische Weise angewendet wird, n flatMapdh verschachtelte Anrufe, die durch einen einzelnen mapAnruf abgeschlossen werden.
Ein erfundenes anschauliches Beispiel soll
die Ausdruckskraft der forSyntax demonstrieren
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 (_ + _)
}
Können Sie die Art von erraten valuesList?
Wie bereits gesagt, wird die Form des monaddurch das Verständnis beibehalten, also beginnen wir mit einem ListIn company.branchesund müssen mit einem enden List.
Der innere Typ ändert sich stattdessen und wird durch den yieldAusdruck bestimmt: welcher istcustomer.value: Int
valueList sollte ein sein List[Int]