TL; DR vá diretamente para o exemplo final
Vou tentar recapitular.
Definições
A forcompreensão é um atalho de sintaxe para combinar flatMape mapde uma forma fácil de ler e raciocinar.
Vamos simplificar um pouco as coisas e assumir que todos os classque fornecem os dois métodos mencionados acima podem ser chamados de a monade usaremos o símbolo M[A]para significar a monadcom um tipo interno A.
Exemplos
Algumas mônadas comumente vistas incluem:
List[String] Onde
M[X] = List[X]
A = String
Option[Int] Onde
Future[String => Boolean] Onde
M[X] = Future[X]
A = (String => Boolean)
mapa e mapa plano
Definido em uma mônada genérica 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]
por exemplo
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)
para expressão
Cada linha na expressão que usa o <-símbolo é traduzida para uma flatMapchamada, exceto para a última linha que é traduzida para uma mapchamada final , onde o "símbolo vinculado" no lado esquerdo é passado como o parâmetro para a função do argumento (o que que chamamos anteriormente 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
Uma for-expression com apenas um <-é convertida em uma mapchamada com a expressão passada como argumento:
// 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
Agora ao ponto
Como você pode ver, a mapoperação preserva a "forma" do original monad, então o mesmo acontece para a yieldexpressão: a Listpermanece Listcom o conteúdo transformado pela operação no yield.
Por outro lado, cada linha de ligação no foré apenas uma composição de sucessivas monads, que devem ser "achatadas" para manter uma única "forma externa".
Suponha por um momento que cada ligação interna foi traduzida para uma mapchamada, mas a mão direita era a mesma A => M[B]função, você terminaria com um M[M[B]]para cada linha na compreensão.
A intenção de toda a forsintaxe é facilmente "nivelar" a concatenação de operações monádicas sucessivas (ou seja, operações que "levantam" um valor em uma "forma monádica":) A => M[B], com a adição de uma mapoperação final que possivelmente executa uma transformação conclusiva.
Espero que isso explique a lógica por trás da escolha da tradução, que é aplicada de forma mecânica, ou seja: n flatMapchamadas aninhadas concluídas por uma única mapchamada.
Um exemplo ilustrativo inventado
destinado a mostrar a expressividade da forsintaxe
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 (_ + _)
}
Você consegue adivinhar o tipo de valuesList?
Como já foi dito, a forma do monadé mantida através da compreensão, então começamos com um Listdentro company.branchese devemos terminar com um List.
Em vez disso, o tipo interno muda e é determinado pela yieldexpressão: que écustomer.value: Int
valueList devia ser um List[Int]