TL;DR aller directement au dernier exemple
Je vais essayer de récapituler
Définitions
L' for
compréhension est une syntaxe raccourcie pour combiner flatMap
et map
d'une manière qui est facile à lire et à comprendre.
Nous allons simplifier les choses un peu et supposons que chaque class
qui fournit à la fois des méthodes ci-dessus peut être appelé un monad
et, nous allons utiliser le symbole M[A]
signifier monad
avec un intérieur de type A
.
Exemples
Certains communément vu monades
-
List[String]
où
-
Option[Int]
où
-
Future[String => Boolean]
où
M[_]: Future[_]
A: String => Boolean
carte et flatMap
Définie dans un générique monade 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]
par exemple
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)
pour l'expression
-
chaque ligne dans l'expression à l'aide de l' <-
symbole est traduite en flatMap
appel où le "lié " symbole" sur le côté gauche est passée comme paramètre à la fonction d'argument (ce que nous avons appelé précédemment f: A => M[B]
)
//writing
for {
bound <- list
out <- f(bound)
} yield out
//is the same as
list flatMap f
-
l' yield
expression est convertie à conclure map
appel à l'expression passée en argument
//writing
for {
bound <- list
} yield f(bound)
//is the same as
list map f
Maintenant, pour le point
Comme vous pouvez le voir, l' map
opération préserve la "forme" de l'original, monad
, de sorte que la même chose se passe pour l' yield
expression: un List
reste List
avec le contenu transformé par l'opération de la yield
D'autre part, chacun de liaison de la ligne dans l' for
est juste une composition de successives monads
, qui doit être "aplati" afin de maintenir une seule "forme extérieure"
Supposons pour un instant que chacun de liaison interne a été traduite en map
appel, mais la droite était le même A => M[B]
fonction, vous vous retrouvez avec un M[M[B]]
pour chaque ligne dans la compréhension.
L'intention de l'ensemble de l' for
syntaxe est facilement "aplatir" la concaténation de successives monadique opérations (opérations que "l'ascenseur" d'une valeur dans une "monadique de la forme: A => M[B]
), avec l'ajout d'un final map
opération qui , éventuellement, effectue une conclusion de transformation
J'espère que c'est ce qui explique la logique derrière le choix de la traduction, qui est appliqué de manière mécanique, c'est - n
flatMap
appels imbriqués conclu par un seul map
appel.
Artificiel, exemple
Destinés à montrer l'expressivité de l' for
de la syntaxe
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)
valueList reduce (_ + _)
}
Pouvez-vous deviner le type d' valuesList
?
Comme déjà dit, la forme de l' monad
est maintenue par le biais de la compréhension, nous commençons donc avec un List
en company.branches
, et doit se terminer par un List
.
L'intérieur type, au lieu des modifications et est déterminé par l' yield
expression: qui est - customer.value: Int
valueList
devrait être un List[Int]