假设我有两个函数来获取订单和订单商品:
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?
你真的希望能够将中间的两层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,它结合了map和sequence操作:
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操作完全符合你的要求.