如何处理Scala中的列表选项?

如何处理Scala中的列表选项?,scala,functional-programming,monads,Scala,Functional Programming,Monads,假设我有一个函数getCustomers和getOrdersByCustomer def getCustomer():List[Customer] = ... def getOrdersByCustomer(cust: Customer): List[Order] = ... 到目前为止还不错,但是如果列表的getCustomer和getOrdersByCustomer返回选项会怎么样 def getCustomer():Option[List[Customer]] = ... def getO

假设我有一个函数
getCustomers
getOrdersByCustomer

def getCustomer():List[Customer] = ... def getOrdersByCustomer(cust: Customer): List[Order] = ... 到目前为止还不错,但是如果列表的
getCustomer
getOrdersByCustomer
返回选项会怎么样

def getCustomer():Option[List[Customer]] = ... def getOrdersByCustomer(cust: Customer): Option[List[Order]] = ... def getCustomer():选项[列表[客户]]=。。。 def getOrdersByCustomer(cust:Customer):选项[列表[订单]]=。。。 现在我想实现两种不同风格的
getOrdersOfAllCustomers()

  • 如果其中一个函数返回None,则返回None
  • 如果
    getCustomer
    返回无,则返回无;如果
    getOrdersByCustomer
    返回无,则不必在意
您建议如何实施它?

试试这个

def getOrdersOfAllCustomers(): Option[List[Order]] =
  for{
    cust <- getCustomer().toList.flatten; 
    order <- getOrderByCustomer(cust).toList.flatten
  } yield order
def getOrdersOfAllCustomers():选项[列表[订单]]=
为了{
cust这应该可以做到:

def getOrdersOfAllCustomers(): Option[List[Order]] = {
  getCustomer() flatMap { customers =>
    //optOrders is a List[Option[List[Order]]]
    val optOrders = customers map { getOrderByCustomer }

    // Any result must be wrapped in an Option because we're flatMapping 
    // the return from the initial getCustomer call
    if(optOrders contains None) None
    else {
      // map the nested Option[List[Order]]] into List[List[Order]]
      // and flatten into a List[Order]
      // This then gives a List[List[Order]] which can be flattened again
      Some(optOrders.map(_.toList.flatten).flatten)
    }
  }
}

困难的部分是处理这样一种情况,即
getOrderByCustomer
的一个嵌套调用返回
None
,并将结果冒泡返回到外部范围(这就是为什么使用空列表更容易的原因)我认为你应该考虑三种可能性——一个填充列表,一个空列表,或者一个错误——并且避免很多不雅的测试来找出哪一个发生。 因此,请将
尝试
列表一起使用

def getOrdersOfAllCustomers(): Try[List[Order]] = {
  Try(funtionReturningListOfOrders())
}
如果一切顺利,您将获得一个
成功[List[Order]]
;否则,
失败[List[Order]]

这种方法的美妙之处在于无论发生什么情况——填充的列表、空列表或错误——你都可以用列表做你想做的所有事情。这是因为
Try
是一个单子,就像
Option
是一个单子一样。继续吧,
filter
forEach
map
,等等,不必在意是什么这三个人中有一个发生了

有一件事很尴尬,那就是你必须弄清楚是成功还是失败。然后使用
match
表达式:

getOrdersOfAllCustomers() match {
  case Success(orders) => println(s"Awww...yeah!")
  case Failure(ex) => println(s"Stupid Scala")
}

即使您不使用
Try
,我也恳请您不要将空列表与填充的列表区别对待。

返回“无”而不是“空列表”真的有意义吗?只是好奇,以防您正在设计此api。我想区分错误和空列表。@Michael但是抛出的错误没有任何区别o与空列表无关。对于错误传播,此处可能更有用的是任何一种类型。
other[Exception,List[Customer]]
是有意义的。
尝试
似乎比
或者
选项
@Dylan更具描述性。我很乐意在另一个问题中讨论
尝试
或者
)谢谢:)但是为ca定义一个特殊的单子
选项列表
,用它自己的
平面图
,不是更容易吗当我不止一次需要此逻辑时,请使用se。更好的方法是使用原始列表和
Nil
,而不是将所有内容包装在选项中。
选项
没有语义含义,因此如果您关心错误,那么您应该使用
Try
或者
(或者Scalaz的
验证
)并详细说明您希望如何处理/聚合错误。与其定义
OptionalList
,不如查看Scalaz中的
OptionT
/
ListT
单子转换器。
Try
不是单子。这与我读过的任何内容都不符。您能提供一些参考吗所以我可以了解更多?这有点吹毛求疵,但是给定
fn:Unit=>Try[Unit]=sys.error(“”
Success(())。flatMap(fn)
fn(())
不同。
getOrdersOfAllCustomers() match {
  case Success(orders) => println(s"Awww...yeah!")
  case Failure(ex) => println(s"Stupid Scala")
}