如何处理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")
}