我正在编写一个以Option[Credentials]
为参数的身份验证客户端。此Credentials
对象上有一个.token
方法,然后我将使用该方法构造一个HTTP请求以发布到端点。这将返回Future[HttpResponse]
,然后我需要验证,解组,然后转换回我的返回类型,即Option[String]
。
我的第一个想法是使用这样的理解:
val resp = for {
c <- creds
req <- buildRequest(c.token)
resp <- Http().singleRequest(req)
} yield resp
然后我发现monad不能像那样组成。我的下一个想法是做这样的事情:
val respFut = Http().singleRequest(buildRequest(token))
respFut.onComplete {
case Success(resp) => Some("john.doe")//do stuff
case Failure(_) => None
}
不幸的是onComplete
会返回一个单元,map
会给我留下Future[Option[String]]
,而我目前知道的剥离未来包装器的唯一方法是使用pipeTo
akka框架中的方法。如何将其转换回选项字符串?
答案 0 :(得分:1)
一旦你获得了Future[T]
,不尝试将其取消包装通常是不错的做法,直到你必须这样做。您可以更改方法以返回Future[Option[String]]
吗?您可以在多长时间内处理掉期货?理想情况下它一直都是。
这样的结果会给你一个Future[Option[String]]
:
val futureResult = creds map {
case Some(c) => {
val req = buildRequest(c.token)
val futureResponse = Http().singleRequest(req)
futureResponse.map(res => Some(convertResponseToString(res)))
}
case None => Future(None)
}
如果确实需要阻止并等待结果,您可以按here.
所述Await.result
进行操作
如果你想以更多的monadic风格(在for-comprehension中,就像你尝试过的那样),cats has an OptionT type将有助于此,我认为scalaz也是如此。但是,您是否想要进入这些库中的任何一个都取决于您。
答案 1 :(得分:1)
很容易升级&#34; Option
到Future[Option[...]]
,因此请使用Future
作为主要的monad。并首先处理更简单的案例:
val f: Future[Option[String]] =
// no credential? just wrap a `None` in a successful future
credsOpt.fold(Future.successful(Option.empty[String])) {creds =>
Http()
.singleRequest(buildRequest(creds.token))
.map(convertResponseToString)
.recover {case _ => Option.empty[String]}
}
将此未来转变为Option[String]
的唯一方法是使用Await.result(...)
等待它......但如果未来可以传递给下一个来电者,那就更好了(不阻挡)。
答案 2 :(得分:1)
我不是百分之百确定你的所有类型是什么,但似乎你想要一个混合选项和未来的理解。我经常遇到这种情况,我发现我可以将我的理解作为一种方法来使代码看起来更好一些。
val resp = for {
c <- creds
req <- buildRequest(c.token)
} yield for {
resp <- Http().singleRequest(req)
} yield resp
resp成为一个Option [Future [HttpResponse]],你可以匹配/ partial func around with None意味着代码永远不会执行,因为它的条件失败了。这是一个愚蠢的小技巧,我用它来使理解看起来更好,我希望它能为你的解决方案提供一个暗示。