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

时间:2016-02-08 09:55:24

标签: 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]
}
  

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

val result = UserService.loadUser("mike")
  .flatMap(user => user.child)
  .flatMap(user => user.child)

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

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

我有一个类型Person,其中Person的存在是可选的,但如果我们有一个人,他的属性就会得到保证。因此,Option类型本身并未使用Person类型。

Person的PID类型为IdId类型包含两种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)

行。这设置了我的人和它的可选实例。

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

val result = operson.flatMap(person => Some(person.pid)).flatMap(pid => Some(pid.idVal)).getOrElse("NoValue")

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

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

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

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

val result = person?.pid.idVal
哇,那好一点!

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

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

  

或者更简洁:

val result = UserService.loadUser("mike")
  .flatMap(_.child)
  .flatMap(_.child)

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

然而,当我用我的例子尝试同样的事情时:

val result = operson.flatMap(Some(_.pid)).flatMap(Some(_.idVal)).getOrElse("NoValue")

斯卡拉抱怨道。

<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")

有人可以帮助我吗?

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

提前感谢!

2 个答案:

答案 0 :(得分:9)

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

val result = operson.map(_.pid).map(_.idVal).getOrElse("NoValue")

甚至更短:

val result = operson.map(_.pid.idVal).getOrElse("NoValue")

您只应将flatMap与返回Option的函数一起使用。您的pididVal不是Option,所以只需映射它们。

你说

  

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

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

flatMap视为map,然后是flatten(因此得名)。如果我映射到孩子:

val ouser = Some(new User())
val child: Option[Option[User]] = ouser.map(_.child)

我最终得到Option[Option[User]]。我需要将其展平为单Option级,这就是我首先使用flatMap的原因。

答案 1 :(得分:7)

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

02-08 11:12:29.126    9112-9112/com.example.emiliomorillanieto.practica3 E/AndroidRuntime﹕ FATAL EXCEPTION: main
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.emiliomorillanieto.practica3/com.example.emiliomorillanieto.practica3.Movimiento}: android.view.InflateException: Binary XML file line #39: Error inflating class <unknown>
 at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2305)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2363)
        at android.app.ActivityThread.access$900(ActivityThread.java:161)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1265)
        at android.os.Handler.dispatchMessage(Handler.java:102)
        at android.os.Looper.loop(Looper.java:157)
        at android.app.ActivityThread.main(ActivityThread.java:5356)
        at java.lang.reflect.Method.invokeNative(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:515)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1265)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1081)
        at dalvik.system.NativeStart.main(Native Method)
 Caused by: android.view.InflateException: Binary XML file line #39: Error inflating class <unknown>
 …
 Caused by: java.lang.reflect.InvocationTargetException
 …
 Caused by: java.lang.OutOfMemoryError
 …

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