我有一个简单的枚举名称失败/成功的结果
sealed trait Result
case object Success extends Result
case object Fail extends Result
并且我有一个什么都不会返回结果的Future的函数,
def do(): Future[Result] = {
someService.getInfo() flatMap { responseInfo =>
responseInfo.fold(Future.successful[Result](Result)) { info =>
myDB.getData(info.resId) map { dbRes =>
if (dbRes.isSomething) Fail else Success
} recover {
case e: NotFoundException => Fail
}
}
}
}
我不喜欢我的解决方案,看起来很混乱,有没有更简单的解决方案?
someService.getInfo
返回Future[Option[ResponseInfo]]
而我想在这里做的是万一someService.getInfo
返回None,那么func应该返回Future [Fail] else来调用db,它也返回case类的Future并基于响应返回Fail /成功。
但是我正在寻找一种超级时尚的scala方法来使其看起来不错:)
谢谢!
答案 0 :(得分:1)
您正在创建Success
已经拥有的Failure
和scala.concurrent.Future
的新概念,因此您所做的工作毫无意义。
您拥有所有可能需要的故障状态表示,因为在Future
中隐藏了scala.util.Try
,因此您已经可以match
,fold
等有正常的流量控制方法。
def do(): Future[Option[DbResult]] = {
someService.getInfo() flatMap { responseInfo =>
responseInfo.fold(Future.successful(_)) { info =>
myDB.getData(info.resId) map { dbRes =>
if (dbRes.isSomething) Some(dbRes) else None
} recover {
case e: NotFoundException => Future.failed(e)
}
}
}
}
合并概念也不是一个好主意。例如,在数据库中针对给定查询“未找到”结果是非常有效的状态,并且与失败的未来无关。
将来发生的故障应表示网络故障,查询格式错误等,而不是数据库正确返回没有结果的有效往返。因此,您可以使用Option
。
Monad变形金刚
这是一个普遍的问题,即将G[F[_]]
转换为F[G[_]]
,在这里您需要处理包装类型的有趣组合。对Future[Option[T]]
说Option[Future[T]]
。处理这些东西的功能性方法有些毛茸茸,但值得。
import cats.data.OptionT
import cats.std.future._
def do() = {
val chain = for {
info <- OptionT(someService.getInfo())
data <- OptionT(myDB.getData(info.resId) map { dbRes =>
if (dbRes.isSomething) Some(dbRes) else None
} recover {
case e: NotFoundException => Future.failed(e)
})
} yield data
chain.value
}
它看起来与上面类似。阅读有关OptionT
here的更多信息,但它看起来像您想要的。