在scala func中处理选项未来的干净方法

时间:2018-06-20 13:50:05

标签: scala

我有一个简单的枚举名称失败/成功的结果

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方法来使其看起来不错:)

谢谢!

1 个答案:

答案 0 :(得分:1)

您正在创建Success已经拥有的Failurescala.concurrent.Future的新概念,因此您所做的工作毫无意义。

您拥有所有可能需要的故障状态表示,因为在Future中隐藏了scala.util.Try,因此您已经可以matchfold等有正常的流量控制方法。

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的更多信息,但它看起来像您想要的。