Scala:链式平面图与嵌套平面图

问题描述 投票:0回答:1

我想知道在 Scala 中,这两段代码之间有什么区别/偏好吗?

// common code
  case class Person(maybeAddress: Option[Address])
  case class Address(maybeZipCode: Option[String])

链式平面图:

  maybePerson
    .flatMap(person => person.maybeAddress)
    .flatMap(address => address.maybeZipCode)

嵌套平面图:

  maybePerson
    .flatMap(person => person.maybeAddress.flatMap(address => address.maybeZipCode))

谢谢!

scala functional-programming
1个回答
0
投票

根据提供的示例,除了样式之外没有太大区别。

如果我们看一下 Option.flatmapscaladoc

  /** Returns the result of applying $f to this $option's value if
   * this $option is nonempty.
   *
   * This is equivalent to:
   * {{{
   * option match {
   *   case Some(x) => f(x)
   *   case None    => None
   * }
   * }}}
   */
  @inline final def flatMap[B](f: A => Option[B]): Option[B] =
    if (isEmpty) None else f(this.get)

如果我们按照scaladoc中显示的模式匹配重写您问题的每个示例,则第一种情况将是

// from
maybePerson
    .flatMap(person => person.maybeAddress)
    .flatMap(address => address.maybeZipCode)

// to
maybePerson match {
  case Some(person) => person.maybeAddress
  case None         => None
} match { // this match will always be applied
  case Some(address) => address.maybeZipCode
  case None          => None
}

第二个是

// from
maybePerson
    .flatMap(person => 
      person.maybeAddress
        .flatMap(address => 
          address.maybeZipCode
        )
    )

// to
maybePerson match {
  case Some(person) => 
        // this match will be applied only if maybePerson is Some
        person.maybeAddress match {
          case Some(address) => address.maybeZipCode
          case None          => None
        }
  case None => None
}

正如我之前详细介绍的,在我们正在分析的示例中,除了样式之外没有太大区别。

也许在更复杂的场景中,您将拥有必须计算的值,其中一些值的处理成本可能很高,或者会产生副作用,如果出现问题,您应该回滚。


话虽如此,从我的角度来看,我更喜欢用于理解

for {
  person  <- maybePerson
  address <- person.maybeAddress
  zipCode <- address.maybeZipCode
} yield zipCode

这是一种更垂直的风格,避免嵌套代码块,除非您将匿名函数提取到像这样的变量中声明的方法或函数

def personToAddress(person: Person): Option[Address] =
  person.maybeAddress

def addressToZipCode(address: Address): Option[String] =
  address.maybeZipCode

// first case
maybePerson
    .flatMap(mapToAddress)
    .flatMap(mapToZipCode)

// second case
def addressToZipCode(address: Address) =
  address.maybeZipCode

def personToZipCode(person: Person) =
  person
    .maybeAddress
    .flatMap(addressToZipCode)

maybePerson
  .flatMap(personToZipCode)
© www.soinside.com 2019 - 2024. All rights reserved.