TL; DR gehen direkt zum letzten Beispiel
Ich werde versuchen, es noch einmal zusammenzufassen.
Definitionen
Das for
Verständnis ist eine Syntax Verknüpfung zu kombinieren flatMap
und map
in 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, monad
und wir verwenden das Symbol M[A]
, um a monad
mit 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 flatMap
Aufruf übersetzt, mit Ausnahme der letzten Zeile, die in einen abschließenden map
Aufruf ü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 map
Aufruf 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 map
Operation die "Form" des Originals bei monad
, so dass dies auch für den yield
Ausdruck gilt: a List
bleibt a List
mit dem durch die Operation in der Datei transformierten Inhalt yield
.
Andererseits ist jede Bindungslinie in der for
nur 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 map
Aufruf ü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 for
Syntax 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 map
Operation 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
flatMap
dh verschachtelte Anrufe, die durch einen einzelnen map
Anruf abgeschlossen werden.
Ein erfundenes anschauliches Beispiel soll
die Ausdruckskraft der for
Syntax 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 monad
durch das Verständnis beibehalten, also beginnen wir mit einem List
In company.branches
und müssen mit einem enden List
.
Der innere Typ ändert sich stattdessen und wird durch den yield
Ausdruck bestimmt: welcher istcustomer.value: Int
valueList
sollte ein sein List[Int]