可以使用Scala的Option flatMap方法更简洁吗?

sva*_*ens 2 scala optional flatmap

我无疑是Scala的新手,我在许多Scala示例中遇到的语法糖问题.它通常会产生一个非常简洁的陈述,但老实说到目前为止(对我而言)有点难以理解.

因此,我希望将Option类(安全解除引用)的典型用法作为开始理解的好地方,例如,在我看到的特定示例中使用下划线.

我发现了一篇非常好的文章,展示了使用Option来避免null的情况.

https://medium.com/@sinisalouc/demystifying-the-monad-in-scala-cc716bb6f534#.fhrljf7nl

他描述了一个用途:

trait User {
  val child: Option[User]
}
Run Code Online (Sandbox Code Playgroud)

顺便说一句,您也可以将这些函数编写为就地lambda函数,而不是先验地定义它们.然后代码变为:

val result = UserService.loadUser("mike")
  .flatMap(user => user.child)
  .flatMap(user => user.child)
Run Code Online (Sandbox Code Playgroud)

那看起来很棒!也许不像人们在groovy中那样简洁,但也不错.

所以我想我会尝试将它应用到我想要解决的案例中.

我有一种类型Person,其中a的存在Person是可选的,但如果我们有一个人,他的属性是有保证的.出于这个原因,没有使用的Option的内型Person类型本身.

Person具有PID它的类型的Id.该Id类型包括两种String类型; Id-Type和Id-Value.

我使用Scala控制台测试以下内容:

class Id(val idCode : String, val idVal : String)

class Person(val pid : Id, val name : String)

val anId: Id = new Id("Passport_number", "12345")

val person: Person = new Person(anId, "Sean")

val operson : Option[Person] = Some(person)
Run Code Online (Sandbox Code Playgroud)

好.设置我的人和它的可选实例.

我从上面的链接文章中了解到,我可以使用flatMap获得Persons Id-Val; 像这样:

val result = operson.flatMap(person => Some(person.pid)).flatMap(pid => Some(pid.idVal)).getOrElse("NoValue")
Run Code Online (Sandbox Code Playgroud)

大!这样可行.如果我事实上没有人,我的结果是"NoValue".

我使用flatMap(而不是Map)因为,除非我误解(并且我的Map测试不正确)如果我使用Map我必须提供备用或默认Person实例.我不想这么做.

好的,所以,flatMap是要走的路.

但是,这真的不是一个非常简洁的陈述.如果我以更加时髦的风格写作,我想我可以做这样的事情:

val result = person?.pid.idVal
Run Code Online (Sandbox Code Playgroud)

哇,这有点好!

当然,Scala有能力提供至少与Groovy一样好的东西吗?

在上面的链接示例中,他能够使用我之前提到的一些语法糖使他的陈述更简洁.下划线:

甚至更简洁:

val result = UserService.loadUser("mike")
  .flatMap(_.child)
  .flatMap(_.child)
Run Code Online (Sandbox Code Playgroud)

因此,在这种情况下,下划线字符似乎允许您跳过指定类型(推断类型)并将其替换为下划线.

但是,当我用我的例子尝试同样的事情时:

val result = operson.flatMap(Some(_.pid)).flatMap(Some(_.idVal)).getOrElse("NoValue")
Run Code Online (Sandbox Code Playgroud)

斯卡拉抱怨道.

<console>:15: error: missing parameter type for expanded function ((x$2) => x$2.idVal)
       val result = operson.flatMap(Some(_.pid)).flatMap(Some(_.idVal)).getOrElse("NoValue")
Run Code Online (Sandbox Code Playgroud)

有人可以帮助我吗?

我怎么会误解这个?是否有一种简短的方法来编写我上面冗长的陈述?flatMap是实现我追求目标的最佳方式吗?或者是否有更简洁和/或可读的方法来做到这一点?

提前致谢!

Zol*_*tán 9

你为什么坚持使用flatMap?我只是用map你的例子代替:

val result = operson.map(_.pid).map(_.idVal).getOrElse("NoValue")
Run Code Online (Sandbox Code Playgroud)

甚至更短:

val result = operson.map(_.pid.idVal).getOrElse("NoValue")
Run Code Online (Sandbox Code Playgroud)

您应该只使用flatMap返回Options的函数.你pididVals不是Options,所以只需要映射它们.

你说

我有一个Person类型,其中Person的存在是可选的,但如果我们有一个人,他的属性是有保证的.因此,在Person类型本身中没有使用Option类型.

这是您的示例和User示例之间的本质区别.在该User示例中,User实例的存在及其child字段都是选项.这就是为什么,要获得一个child,你需要flatMap.但是,因为在您的示例中,只有a的存在Person无法保证,在您检索到之后Option[Person],您可以安全地映射到其任何字段.

可以想象flatMap为a map,然后是a flatten(因此得名).如果我映射到孩子:

val ouser = Some(new User())
val child: Option[Option[User]] = ouser.map(_.child)
Run Code Online (Sandbox Code Playgroud)

我最终会得到一个Option[Option[User]].我需要将其压缩到一个单一的Option水平,这就是我flatMap首先使用的原因.


Nya*_*vro 7

如果您正在寻找最简洁的解决方案,请考虑以下事项:

val result = operson.fold("NoValue")(_.pid.idVal)
Run Code Online (Sandbox Code Playgroud)

虽然人们可能会发现它不清楚或令人困惑