【问题标题】:How to get Future[\/[Throwable, T]] from several Future[\/[Throwable, T]]如何从几个 Future[\/[Throwable, T]] 中获取 Future[\/[Throwable, T]]
【发布时间】:2016-02-24 00:11:15
【问题描述】:

假设我有几种方法可以返回\/[Throwable, String]。右边的值Int是我想要的,左边的值会累积错误。

import scalaz._
import Scalaz._
type ErrorOr[T] = \/[Throwable, T]

def init(a: Int): ErrorOr[Int] = a.right
def add(a: Int, b: Int): ErrorOr[Int] = (a + b).right
def multiply(a: Int, b: Int): ErrorOr[Int] = (a * b).right

init(3).flatMap(add(_, 4)).flatMap(multiply(_, 3)) // 21

看起来不错,因为 scalaz any 的平面图是右偏的,所以它会根据正确的值进行操作。

但是,如果要更改 API 调用的方法,则返回类型将为 Future[ErrorOr[T]]。有什么方法可以用来返回Future[ErrorOr[T]]?另外,我可能想使用future.flatMap 作为回调,而不是在这里使用await 来阻止

def init(a: Int): Future[ErrorOr[Int]] = Future(a.right)
def add(a: Int, b: Int): Future[ErrorOr[Int]] = Future((a + b).right)
def multiply(a: Int, b: Int): Future[ErrorOr[Int]] = Future((a * b).right)

【问题讨论】:

    标签: scala scalaz


    【解决方案1】:

    您可以像现在一样将init 的结果包装在EitherT monad 转换器中,使用flatMapF 而不是flatMap 直接使用addmultiply,然后调用run回复Future[ErrorOr[Int]]

    EitherT(init(3)).flatMapF(add(_, 4)).flatMapF(multiply(_, 3)).run
    

    【讨论】:

    • 这个答案会更好地命名所有涉及的类别,以及它们来自哪里。就像 EitherT 是一个 monad 转换器,就像已经完成的那样,但也适用于 flatMapF,以及无论 run 是什么。
    【解决方案2】:

    你可以考虑使用scalaz.concurrent.Task:

    def init(a: Int): Task[Int] = Task.now(a)
    def add(a: Int, b: Int): Task[Int] = Task.now(a + b)
    def multiply(a: Int, b: Int): Task[Int] = Task.now(a * b)
    

    所以:

    scala> val res = init(3).flatMap(add(_, 4)).flatMap(multiply(_, 3))
    res: scalaz.concurrent.Task[Int] = scalaz.concurrent.Task@3fdb3076
    

    可以轻松转换为:

    scala> res.get
    res1: scalaz.concurrent.Future[scalaz.\/[Throwable,Int]] = ...
    
    scala> res.get.run
    res5: scalaz.\/[Throwable,Int] = \/-(21)
    

    如果你需要失败:

    scala> def add(a: Int, b: Int): Task[Int] = Task.fail(new RuntimeException("fail"))
    add: (a: Int, b: Int)scalaz.concurrent.Task[Int]
    
    scala> init(3).flatMap(add(_, 4)).flatMap(multiply(_, 3)).get.run
    res4: scalaz.\/[Throwable,Int] = -\/(java.lang.RuntimeException: fail)
    

    请注意,这里的Futurescalaz.concurrent.Future

    更新

    如果你需要将Either 传递给任务 - 你可以使用这个:

    import Task._
    
    implicit class FutureToTask[T](f: Future[\/[Throwable, T]]){
       def task = async(f.runAsync)
    }
    

    【讨论】:

    • 我喜欢用Task。但是,这也意味着我需要在我的代码中抛出异常,而不是将其作为一个左值来捕获。你认为这是个好主意吗?
    • import Task._; Task(5.right.fold(fail, now)).join 这样的东西可能会转换它,或者你可以使用Task.async
    • 我同意它看起来很棘手,我希望我们可以从 comig fs2 库中获得更方便的任务
    猜你喜欢
    • 2015-01-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-08
    • 1970-01-01
    • 1970-01-01
    • 2019-10-25
    • 1970-01-01
    相关资源
    最近更新 更多