Enr*_*ina 6 functional-programming scala category-theory scala-cats
通过使用Scala和Cats(或者可能是另一个专注于类别理论和/或函数式编程的库),以最具功能性(代数)的方式解决这个问题的最佳方法是什么?
如果我们有以下方法执行REST API调用来检索单个信息?
type FutureApiCallResult[A] = Future[Either[String, Option[A]]]
def getNameApiCall(id: Int): FutureApiCallResult[String]
def getAgeApiCall(id: Int): FutureApiCallResult[Int]
def getEmailApiCall(id: Int): FutureApiCallResult[String]
Run Code Online (Sandbox Code Playgroud)
如您所见,它们会产生异步结果.Either monad用于在API调用期间返回可能的错误,而Option用于在API未找到资源时返回None(这种情况不是错误,而是可能的和期望的结果类型).
case class Person(name: String, age: Int, email: String)
def getPerson(id: Int): Future[Option[Person]] = ???
Run Code Online (Sandbox Code Playgroud)
如果任何API调用失败或任何API调用返回None(整个Person实体无法组合),则此方法应使用上面定义的三个API调用方法异步组合并返回Person或None.
出于性能原因,所有API调用必须以并行方式完成
我认为最好的选择是使用Cats Semigroupal Validated但是在尝试处理Future和如此多的嵌套Monads时我迷路了:S
任何人都可以告诉我你将如何实现这一点(即使改变方法签名或主要概念)或指向我正确的资源?我在编码时对Cats和Algebra很新,但我想学习如何处理这种情况,以便我可以在工作中使用它.
Bar*_*ski 18
这里的关键要求是它必须并行完成.这意味着使用monad的明显解决方案是out,因为monadic bind是阻塞的(它需要结果以防它必须在它上分支).所以最好的选择是使用applicative.
我不是Scala程序员,因此我无法向您展示代码,但我的想法是应用程序仿函数可以提升多个参数的函数(常规函子提升单个参数的函数使用map).在这里,你可以使用类似的东西map3解除三个参数的构造函数Person来处理三个FutureResults.搜索"Scala中的应用未来"会有一些点击.也有对应用性的实例Either和Option和,不像单子,applicatives可以一起轻松组成.希望这可以帮助.
您可以使用cats.Parallel类型类.这使得一些非常整齐的组合器EitherT能够在并行运行时累积误差.因此,最简单,最简洁的解决方案是:
type FutureResult[A] = EitherT[Future, NonEmptyList[String], Option[A]]
def getPerson(id: Int): FutureResult[Person] =
(getNameApiCall(id), getAgeApiCall(id), getEmailApiCall(id))
.parMapN((name, age, email) => (name, age, email).mapN(Person))
Run Code Online (Sandbox Code Playgroud)
有关Parallel访问cats文档的更多信息.
编辑:这是没有内在的另一种方式Option:
type FutureResult[A] = EitherT[Future, NonEmptyList[String], A]
def getPerson(id: Int): FutureResult[Person] =
(getNameApiCall(id), getAgeApiCall(id), getEmailApiCall(id))
.parMapN(Person)
Run Code Online (Sandbox Code Playgroud)