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)
地图和平面地图
在通用单子中定义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
表达式也是如此: aList
仍然是 a List
,其内容由yield
.
另一方面,中的每条装订线for
只是连续的组合monads
,必须“压平”以保持单一的“外部形状”。
假设每个内部绑定都被转换为一个map
调用,但右手是同一个A => M[B]
函数,你最终会在推导中得到一个M[M[B]]
for each line。
整个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
吗?
如前所述,the 的形状monad
是通过推导来维持的,所以我们从 a List
in开始company.branches
,并且必须以 a 结束List
。
相反,内部类型会发生变化并由yield
表达式确定:customer.value: Int
valueList
应该是一个List[Int]