如何编写在Scala中返回Option [List]的函数?

Mic*_*ael 3 monads scala

假设我有两个函数来获取订单和订单商品:

def getOrders(): Option[List[Int]] = ...
def getOrderItems(orderId: Int): Option[List[Int]] = ...

请注意,Option[List]由于每个函数都可能失败,因此两个函

现在,我想获得OptionList所有订单项目如下:

  • 返回Some[List]如果这两个函数返回Some
  • None 如果其中任何一个返回None.

我尝试用这些功能组成for(见下文),但它没有用.

val allOrderItems = for {
  orderIds   <- getOrders();
  orderId    <- orderIds;
  orderItems <- getOrderItems(orderId)
} yield orderItems

我怎样才能建立一个功能getAllOrderItems():Option[List[Int]]使用功能getOrdersgetOrderItems

Tra*_*own 6

你真的希望能够将中间的两层Option[List[Option[List[Int]]]]内部翻出来,这样你就可以获得彼此相邻的选项和列表.此操作称为排序,由Scalaz提供:

import scalaz._, Scalaz._

val items: Option[List[Int]] =
  getOrders.flatMap(_.map(getOrderItems).sequence).map(_.flatten)
Run Code Online (Sandbox Code Playgroud)

您可以等效地使用traverse,它结合了mapsequence操作:

val items: Option[List[Int]] =
  getOrders.flatMap(_ traverse getOrderItems).map(_.flatten)
Run Code Online (Sandbox Code Playgroud)

如果您不想使用Scalaz,您可以编写自己的(更少多态)sequence:

def sequence[A](xs: List[Option[A]]) = xs.foldRight(Some(Nil): Option[List[A]]) {
  case (Some(h), Some(t)) => Some(h :: t)
  case _ => None
}
Run Code Online (Sandbox Code Playgroud)

然后:

val items: Option[List[Int]] = getOrders.flatMap(
  orderIds => sequence(orderIds.map(getOrderItems))
).map(_.flatten)
Run Code Online (Sandbox Code Playgroud)

monad转换解决方案实际上非常简单(如果您愿意使用Scalaz):

val items: Option[List[Int]] = (
  for {
    orderId <- ListT(getOrders)
    itemId  <- ListT(getOrderItems(orderId))
  } yield itemId
).underlying
Run Code Online (Sandbox Code Playgroud)

这种方法的好处是你不必考虑你需要展平,顺序等等 - 普通的monadic操作完全符合你的要求.