【问题标题】:Composing Multiple Futures and Option in Scala with ZIO使用 ZIO 在 Scala 中组合多个期货和期权
【发布时间】:2020-07-17 15:07:12
【问题描述】:

我刚刚开始评估 ZIO,以改进我的异步 Scala 代码的编程模型和性能。在我的代码库中,我经常处理Future[Option[T]],到目前为止,我已经使用Scalaz 的OptionT monad 转换器处理了这个问题。现在我想用 ZIO 试试这个。

考虑两个函数:

def foo: String => Future[Option[T]]def bar: T => Future[U]

我尝试过这样的事情:

val t = for {
       o: Option[Int] <- ZIO.fromFuture { implicit ec =>
            foo("test")
       }
       i: Int <- ZIO.fromOption(o)
       s: String <- ZIO.fromFuture { implicit ec =>
            bar(i)
       }
} yield s

根据我的 IDE,t 在这种情况下属于 ZIO[Any, Any, String] 类型。我不知道该怎么办。

我想考虑三种可能性:

  • “成功”案例,foo 生成 Some,可以与值上的其他函数组合
  • foo 产生None 的情况
  • 任一函数产生错误的情况

我不确定如何使用 ZIO 解析这种情况下的这些可能性。任何帮助表示赞赏。

【问题讨论】:

    标签: scala functional-programming scalaz zio


    【解决方案1】:

    ZIO.fromOption(o) 的类型是IO[Unit, A],即ZIO[Any, Unit, A],而ZIO.fromFuture 的类型是Task[A],即ZIO[Any, Throwable, A],如Type Aliases 所述。因此类型不对齐

    ZIO[Any, Unit, A]
    ZIO[Any, Throwable, A]
    

    尝试mapError 将错误类型与Throwable 对齐,就像这样

    for {
      o <- ZIO.fromFuture { implicit ec => foo("test") }
      i <- ZIO.fromOption(o).mapError(_ => new RuntimeException("boom"))
      s <- ZIO.fromFuture { implicit ec => bar(i)}
    } yield s
    

    【讨论】:

    • 这看起来更像是一种引入Exception(或更准确地说是Throwable)的技巧,其唯一目的是对齐类型以使for理解工作。虽然我很欣赏这个答案,但它并不是很有用。这意味着在for 理解之外有一种更惯用的方式来处理这种情况。此外,您如何处理None 案?您如何处理foobar 中出现“真实”错误的可能性?
    • 这不是 hack - 如果你想组合计算,它们的类型必须对齐。 ZIO 在这方面与 Either 没有太大区别。 ZIO 中“神奇地”对齐的是输入/环境。其他参数将寻找要向上转换的通用类型,这为您提供了它们中的任何一个完全不相关的(例如错误参数中的 Option 和 Throwable)。
    • 是的,引入RuntimeException的目的和你描述的完全一样。我所说的“hack”是指RuntimeException 的存在不是为了增加商业价值,而是为了让编程模型发挥作用。只要你打电话给mapError,你做什么都没关系。感觉不是很厉害。但是,是的,如果你想用for comp 解决这个问题,你需要这样做,尽管我在解析这三个场景时仍然没有答案
    • 随着 ZIO v1.x 的发布,文档得到了改进,现在 shows @paulpdaniels 复制 Option[T]] 的解决方案是完全正确的,这对于我们给出的原因是有道理的跨度>
    【解决方案2】:

    在这种情况下,有几个运算符可以帮助您,基本上不是用fromOption 显式解开Option,我建议使用someasSomeError 的组合

    val t: Task[Option[String]] = (for {
    
      // This moves the `None` into the error channel
      i: Int <- ZIO.fromFuture(implicit ec => foo("test")).some
    
      // This wraps the error in a Some() so that the signature matches
      s: String <- ZIO.fromFuture(implicit ec => bar(i)).asSomeError
    
    } yield s).optional // Unwraps the None back into the value channel
    

    【讨论】:

    • 这太棒了。优雅、惯用,并且看起来非常符合我仍在努力理解的 ZIO 心智模型。谢谢。你的 RxJS 书很棒
    • 在相关说明中,如果我有一个函数Task[Option[T]] =&gt; Task[U],我可以在其中生成U,无论T 是否存在,是否有一个ZIO API 调用来帮助实现这一点?还是只是简单地在Task 上进行映射,然后在Option 上进行内部映射(和getOrElse-ing)?
    • @Vidya 我不知道,如果你有一个转换函数,我可能会直接将任务传递给它,即transform(t)
    • 对于那些碰巧遇到这个问题的人,只是想让您知道文档已经随着 ZIO 1.x 和 shows that this answer is indeed the correct one 的发布而扩展,尽管投票数很多。
    猜你喜欢
    • 2016-05-02
    • 2013-10-18
    • 1970-01-01
    • 2017-11-18
    • 1970-01-01
    • 1970-01-01
    • 2016-11-05
    • 2019-03-23
    • 1970-01-01
    相关资源
    最近更新 更多