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
理解によって維持されるため、List
in company.branches
で始まり、で終わる必要がありList
ます。
代わりに、内部の型が変化し、次のyield
式によって決定されます。customer.value: Int
valueList
でなければなりません List[Int]