TL; DRは最後の例に直接進みます
おさらいしてみます。
定義
for理解が結合する構文のショートカットであるflatMapとmapおよそ読みやすいとの理由だ道インチ
少し単純化classして、前述の両方のメソッドを提供するものはすべてa monadと呼ぶことができ、シンボルM[A]を使用monadして内部タイプのaを意味するとしAます。
例
一般的に見られるモナドには次のものがあります。
List[String] どこ
M[X] = List[X]
A = String
Option[Int] どこ
Future[String => Boolean] どこ
M[X] = Future[X]
A = (String => Boolean)
地図とflatMap
ジェネリックモナドで定義 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]
例えば
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)
表現のために
<-記号を使用する式の各行はflatMap呼び出しに変換されますが、最後の行は終了map呼び出しに変換されますが、左側の「バインドされた記号」がパラメーターとして引数関数に渡されます(what以前に呼び出しました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
1つだけのfor式<-はmap、式が引数として渡される呼び出しに変換されます。
// 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
さてポイントに
あなたが見ることができるように、map操作は、元の「形状」を保持するmonad同じことが起こるのためので、yield:表現ListのままListでの操作により変換コンテンツを持ちますyield。
一方、の各結合線は、for連続したの構成にすぎずmonads、単一の「外部形状」を維持するために「平坦化」する必要があります。
少しの間、各内部バインディングがmap呼び出しに変換されたと仮定しますが、右辺は同じA => M[B]関数M[M[B]]であり、内包表記の各行にa を使用することになります。構文
全体の意図は、for連続するモナド演算(つまり、「モナド形状」の値を「持ち上げる」演算)の連結を簡単に「フラット化」しA => M[B]、最終的な変換を実行する可能性のある最終map演算を追加することです。
これが、機械的な方法で適用される変換の選択の背後にあるロジック、つまりn flatMap、単一のmap呼び出しによって終了するネストされた呼び出しが説明されることを願っています。
構文の
表現力を示すために考案された実例for
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 (_ + _)
}
あなたはタイプを推測できますかvaluesList?
すでに述べたように、の形はmonad理解によって維持されるため、Listin company.branchesで始まり、で終わる必要がありListます。
代わりに、内部の型が変化し、次のyield式によって決定されます。customer.value: Int
valueList でなければなりません List[Int]