假设我有两个功能来获取订单和订单商品:
def getOrders(): Option[List[Int]] = ... def getOrderItems(orderId: Int): Option[List[Int]] = ...
请注意,两个函数都返回Option[List]
,因为每个函数都可能失败。
现在,我想获得Option
所有订单商品List
,如下所示:
Some[List]
和,则Some
None
如果其中任何一个返回None。 我尝试使用for
(见下文)撰写这些功能,但它不起作用。
val allOrderItems = for { orderIds <- getOrders(); orderId <- orderIds; orderItems <- getOrderItems(orderId) } yield orderItems
如何使用函数getAllOrderItems():Option[List[Int]]
和getOrders
构建函数getOrderItems
?
答案 0 :(得分:6)
你真的希望能够将Option[List[Option[List[Int]]]]
的中间两层内部翻出来,这样你就可以获得彼此相邻的选项和列表。此操作称为排序,由Scalaz提供:
import scalaz._, Scalaz._
val items: Option[List[Int]] =
getOrders.flatMap(_.map(getOrderItems).sequence).map(_.flatten)
您可以等效地使用traverse
,它结合了map
和sequence
操作:
val items: Option[List[Int]] =
getOrders.flatMap(_ traverse getOrderItems).map(_.flatten)
如果您不想使用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
}
然后:
val items: Option[List[Int]] = getOrders.flatMap(
orderIds => sequence(orderIds.map(getOrderItems))
).map(_.flatten)
Monad转换解决方案实际上非常简单(如果您愿意使用Scalaz):
val items: Option[List[Int]] = (
for {
orderId <- ListT(getOrders)
itemId <- ListT(getOrderItems(orderId))
} yield itemId
).underlying
这种方法的好处在于你不必考虑你需要压扁,顺序等等 - 普通的monadic操作完全符合你的要求。
答案 1 :(得分:2)
我能想到的最简单的修改如下:
for{
orderId <- getOrders.getOrElse(Nil)
items <- getOrderItems(orderId)
} yield items
for comprehension使用第一个语句来确定其余的类型。例如,在上文中,List[Int]
类型将被推测,这与Option[List[Int]]
不同。