TL; DR直接转到最后一个示例
我会尝试回顾一下。
定义
的for
理解是一个语法快捷方式相结合flatMap
,并map
以一种易于阅读和推理。
让我们稍微简化一下,并假设class
提供上述两种方法的每一个都可以称为a,monad
并且我们将使用符号M[A]
表示monad
具有内部类型的a A
。
例子
一些常见的monad包括:
List[String]
哪里
M[X] = List[X]
A = String
Option[Int]
哪里
Future[String => Boolean]
哪里
M[X] = Future[X]
A = (String => Boolean)
地图和flatMap
在通用monad中定义 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
调用除外,在最后一行中,左侧的“绑定符号”作为参数传递给参数函数(我们之前称为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
只有一个表达式的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
表达式也发生了同样的情况:a List
仍然是a 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
,必须以a结尾List
。
内部类型改为更改,并由以下yield
表达式确定:customer.value: Int
valueList
应该是 List[Int]