TL; DR passe directement à l'exemple final
Je vais essayer de récapituler.
Définitions
La forcompréhension est un raccourci syntaxique à combiner flatMapet mapd'une manière facile à lire et à raisonner.
Simplifions un peu les choses et supposons que tout ce classqui fournit les deux méthodes susmentionnées peut être appelé a monadet nous utiliserons le symbole M[A]pour signifier a monadavec un type interne A.
Exemples
Certaines monades couramment observées comprennent:
List[String] où
M[X] = List[X]
A = String
Option[Int] où
Future[String => Boolean] où
M[X] = Future[X]
A = (String => Boolean)
map et flatMap
Défini dans une monade générique 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 de l'expression utilisant le <-symbole est traduite en flatMapappel, à l'exception de la dernière ligne qui est traduite en mapappel final , où le "symbole lié" sur le côté gauche est passé comme paramètre à la fonction d'argument (quel nous avons précédemment appelé 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
Une expression for avec un seul <-est convertie en mapappel avec l'expression passée en argument:
// 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
Maintenant au point
Comme vous pouvez le voir, l' mapopération préserve la "forme" de l'original monad, il en va de même pour l' yieldexpression: a Listreste a Listavec le contenu transformé par l'opération dans le yield.
D'autre part, chaque ligne de reliure dans le forest juste une composition successive monads, qui doit être "aplatie" pour conserver une seule "forme externe".
Supposons un instant que chaque liaison interne soit traduite en mapappel, mais que la main droite ait la même A => M[B]fonction, vous vous retrouveriez avec un M[M[B]]pour chaque ligne dans la compréhension.
L'intention de toute la forsyntaxe est de facilement "aplatir" la concaténation d'opérations monadiques successives (c'est-à-dire des opérations qui "soulèvent" une valeur dans une "forme monadique" :) A => M[B], avec l'ajout d'une mapopération finale qui effectue éventuellement une transformation finale.
J'espère que cela explique la logique du choix de la traduction, qui est appliquée de manière mécanique, c'est-à-dire: n flatMapdes appels imbriqués conclus par un seul mapappel.
Un exemple illustratif artificiel
destiné à montrer l'expressivité de la forsyntaxe
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 (_ + _)
}
Pouvez-vous deviner le type de valuesList?
Comme déjà dit, la forme du monadest maintenue à travers la compréhension, donc nous commençons par un Listin company.branches, et devons finir par un List.
Le type interne change à la place et est déterminé par l' yieldexpression: qui estcustomer.value: Int
valueList devrait être un List[Int]