2.4 ZIO类型参数

我们之前说过,类型 ZIO [R,E,A]的值是一种 functional effect,需要环境R,并且可能因E失败或成功返回A。
现在,我们也了解了ZIO effect 成为并发工作流程的蓝图意味着什么,以及如何组合效果,下面让我们详细讨论每个ZIO类型参数:

  • R是 effect 要执行效果所需的环境。一般指的是 effect 需要的一些依赖关系,例如访问数据库或日志记录服务,或者效果可能不需要任何环境,在这种情况下,类型参数将为Any
  • E是效果可能失效的值的类型。这可能是Throwable或Exception,但也可能是特定领域的错误类型,或者效果可能根本无法失败,在这种情况下,type参数将为Nothing
  • A是效果可以成功的值的类型。可以将其视为效果的返回值或输出。
    理解这些类型参数的一种有用方法是将ZIO effect 想象为函数R => Either [E,A]。这并不是ZIO的实现方式(此定义不允许我们编写并发,异步或资源安全的代码
    算子),但这是一个有用的思维模型。

以下代码段定义了这种 ZIO effect 的 toy model:

  final case class ZIO[-R, +E, +A](run: R => Either[E, A])

从该定义可以看出,R参数是输入(为了执行效果,必须提供R类型的值),而E和A参数是输出。 输入被声明为逆变的,而输出被声明为协变的。有关 variance 的更详细讨论,请参见附录。 否则,只要知道Scala variance注释会改善类型推断,这就是ZIO使用它们的原因。

让我们看看如何使用这种思维模型来实现一些基本的构造函数和运算符:

final case class ZIO[-R, +E, +A](run: R => Either[E, A]) {self =>def map[B](f: A => B): ZIO[R, E, B] = ZIO(r => self.run(r).map(f))def flatMap[R1 <: R, E1 >: E, B](f: A => ZIO[R1, E1, B]): ZIO[R1, E1, B] = ZIO(r => self.run(r).fold(ZIO.fail(_), f).run(r))
}object ZIO {def effect[A](a: => A): ZIO[Any, Throwable, A] = ZIO(_ =>try Right(a)catch {case t: Throwable => Left(t)})def fail[E](e: => E): ZIO[Any, E, Nothing] = ZIO(_ => Left(e))
}

ZIO.effect方法将代码块包装在一个effect中,将异常转换为Left值,并将成功转换为Right值。请注意,ZIO.effect的参数是按名称命名的(使用 :=> 符号),这可以防止对代码进行急切的计算,从而允许ZIO创建描述执行的值。
我们还实现了前面讨论的flatMap运算符,该运算符使我们可以顺序组合效果。 flatMap的实现如下:

  1. 首先使用环境R1运行原始效果,以生成Either [E,A]
  2. 如果原始效果因Left(e)失败,它将立即以Left(e)返回此失败。
  3. 如果原始效果以Right(a)成功,它将在该a上调用f以产生新效果。然后,它将在所需的环境R1中运行该新效果。
    如上所述,ZIO effect 实际上并不是这样实现的,但是执行一个效果,获取其结果然后将其传递给下一个效果的基本思想是一个准确的心理模型,它将帮助你在使用zio的过程中思考。
    我们将很快学习更多的方法来处理成功的ZIO值,但是现在让我们着重研究错误和环境类型,以建立其对他们的直觉,因为他们可能不那么直观。

2.4.1 错误类型

错误类型表示效果潜在的失败原因。 错误类型很有用,因为它允许我们使用类似 flatMap 这样的运算符来处理成功的返回值,同时将错误处理推迟到更高级别。 这使我们能够专注于程序的“快乐之路”并在正确的地方处理错误。
例如,假设我们要编写一个简单的程序,该程序从用户那里获得两个数字并将它们相乘:

import zio._
lazy val readInt: ZIO[Any, NumberFormatException, Int] =???
lazy val readAndSumTwoInts: ZIO[Any, NumberFormatException, Int] = for {x <- readInty <- readInt
} yield x * y

请注意,readInt的返回类型为ZIO [Any,NumberFormatException,Int],指示它不需要任何环境,并且如果成功的话,可以返回整数,如果失败,则抛出NumberFormatException。
错误类型的第一个好处是,我们仅凭其签名就知道该函数将如何失败。我们对readInt的实现一无所知,但仅查看类型签名,我们就知道它可能因NumberFormatException而失败,也不会因任何其他错误而失败。这非常强大,因为我们确切知道潜在的错误类型,而且我们永远不必借助“防御性编程”来处理未知错误。第二个好处是,假设效果成功,我们就可以对效果结果进行操作,将错误处理推迟到以后。
如果两个readInt调用均失败,并出现NumberFormatException,则readAndSumTwoInts也将失败,并出现异常,并中止求和。此簿记将自动为我们处理。我们可以直接将x和y相乘,而不必显式地处理失败的可能性。这将错误处理逻辑推迟给调用方,后者可以重试,报告或推迟更高级别的处理。
能够了解效果可能如何失败并将错误推迟到应用程序的更高级别很有用,但是在某些时候,我们需要能够处理一些或所有错误。

要处理我们的ZIO玩具模型中的错误,我们实现一个名为foldM的运算符,如果原始效果失败,它将使我们执行一个效果,如果成功,则将执行另一个操作:

  final case class ZIO [-R, +E, +A](run : R => Either [E, A]) {self =>def foldM [R1 <: R, E1, B](failure : E => ZIO [R1, E1, B],success : A => ZIO [R1, E1, B]) : ZIO [R1, E1, B] = ZIO(r => self.run(r).fold(failure, success).run(r))}

实际上,该实现与我们上面介绍的flatMap非常相似。 我们只是使用failure函数在出现错误的情况下返回新的效果,然后运行该效果。
错误类型最有用的功能之一是能够使指定效果完全不会失败,这可能是因为已经捕获并处理了它的错误。
在ZIO中,我们通过指定Nothing作为错误类型来做到这一点。 由于没有Nothing类型的值,因此我们知道,如果我们有Either [Nothing,A],则它必须是Right。 我们可以使用它来实现错误处理运算符,这些运算符可以让我们静态地证明效果不会因为我们已经处理了所有错误而失败。

2.4.2 环境类型

现在,我们对错误类型有了一些印象,接下来让我们关注环境类型。
我们可以通过Any来为任何不需要环境的effect进行建模。毕竟,如果effect需要类型为Any的值,则可以使用 ()(单位值),42或任何其他值来运行效果。 因此,可以使用任何类型的值运行的 effect 实际上是不需要任何特定类型环境的 effect。
使用环境的两个基本操作是访问环境(例如,获得对数据库的访问以执行某些操作)和提供环境(在测试的时候提供测试环境,在生产的时候提供生产环境)。

我们可以在ZIO的玩具模型中实现这一点,如以下代码片段所示:

  final case class ZIO[-R, +E, +A](run: R => Either[E, A]) {self =>def provide(r: R): ZIO[Any, E, A] = ZIO(_ => self.run(r))}object ZIO {def environment[R]: ZIO[R, Nothing, R] =ZIO(r => Right(r))}

如您所见,provider运算符会返回不需要任何环境的新效果。 环境构造函数使用所需的环境类型创建一个新效果,并将其作为成功值传递给环境。 这使我们可以访问环境并使用其他运算符(例如map和flatMap)进行处理。

2.4 zio入门——ZIO类型参数相关推荐

  1. 2.5 zio入门——ZIO类型别名

    2.5 ZIO类型别名 ZIO的三个类型参数,功能极为强大.我们可以使用环境类型参数进行参数注入(数据库,连接池,配置等),并且可以使用错误和成功类型参数返回结果. 在最常见的情况下,程序需要向下传播 ...

  2. 2.10 zio入门——总结

    2.10 总结 函数式作用是并发工作流的蓝图,不可变的值提供了多种运算符,用于转换和组合effect以解决更复杂的问题. ZIO类型参数使我们可以对需要上下文提供环境才能执行的作用进行建模:它们使我们 ...

  3. 2.1 zio入门——把函数作用作为工作蓝图

    ZIO标准库的核心数据类型是ZIO[R, E, A],这种类型的值被称为函数式作用 函数式作用是并发工作流的一种蓝图,如图1所示.该蓝图本质上是纯描述性的,必须执行才能观察到任何副作用,例如与数据库的 ...

  4. 第二章 zio 入门

    首先祝贺你开始正式步入研究zio的旅程! ZIO将帮助您构建并发,有弹性,高效且易于理解和测试的现代应用程序. 但是学习ZIO要求以一种全新的角度来思考软件-从函数式编程的角度. 这一章会教会你一些理 ...

  5. 2.6 zio入门——对比Future

    2.6 对比Future 本小节,我们可以通过比较Scala标准库中的ZIO和Future来阐明到目前为止所学的知识. 在本书后面讨论并发性时,我们将讨论ZIO和Future之间的其他差异,但目前要记 ...

  6. 2.8 zio入门——标准ZIO服务

    2.8 标准ZIO服务 在本章的前面,我们讨论了ZIO环境类型,但是我们还没有使用它来编写任何程序. 在本书的后面,我们将深入介绍环境,并说明该环境如何为依赖项注入问题提供全面的解决方案. 现在,我们 ...

  7. 2.7 zio入门——更多的Effect构造函数

    2.7 更多的Effect构造函数 在本章的前面,我们了解了如何使用ZIO.effect构造函数将过程代码转换为ZIO效果.ZIO.effect构造函数是一种有用且通用的效果构造函数,但并不适合所有情 ...

  8. 2.3 zio入门——一些常见的zio操作符

    2.3 其他的一些顺序操作符 按顺序组合是functional effect 最基本的操作,ZIO提供了各种相关的运算符来满足通用需求. zipWith 最基本的是zipWith,它将顺序地合并两个e ...

  9. 2.2 zio入门——按顺序组合ZIO

    2.2 按顺序组合ZIO 正如上小节所述,ZIO效果是描述并发工作流的蓝图,并且我们通过转换和组合更小,更简单的效果来构建更复杂的效果,从而解决业务问题. 我们看到了如何使用 ZIO 的 delay ...

最新文章

  1. UA PHYS515A 电磁理论III 静磁学问题3 静磁学问题的边界条件与标量势方法的应用
  2. java简单通讯录的实现02person类_Java中Math类的简单介绍
  3. 转载一篇《Redis源码研究—哈希表》重点是如何重新哈希
  4. android tab 切换动画,Android之ViewPager+TabLayout组合实现导航条切换效果(微信和QQ底部多标签切换)...
  5. Android--使用Camera拍照
  6. Coaching 企业教练
  7. oracle 表复制 long,关于oracle的数据库的数据Long和Number的转化字段
  8. 基于HT for Web矢量实现3D叶轮旋转
  9. vue2.0 axios封装
  10. 官方win10安装教程,win10系统一键安装方法
  11. “二选一”是支付宝首创,背后是帝国的霸权逻辑
  12. DETR | 基于匈牙利算法的样本分配策略
  13. 谈谈win10的简单美化
  14. 使用ipp静态库,ipp-samples在linux下的make过程
  15. Linux就业技术指导(四):企业CDN缓存加速原理解密
  16. Nacos服务---主流配置中心对比
  17. [Python3]Python官方文档-Python Manuals
  18. Code Review Checklist
  19. 微信中域名网站域名被封锁、被屏蔽、被和谐后的解决方法
  20. 单项目多JDBC驱动版本加载

热门文章

  1. 健身房小姐姐爆满了,我只用PR做了这一件事!
  2. RS485——RS485通信基础理论与STM32测试
  3. idea插件开发icon设计
  4. python编程*三角形图形创意图片_使用python的pil制作微信九宫格图片
  5. 令人心动的HTTP知识点大全
  6. GPT格式的磁盘扩容
  7. 光致变色染料的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告
  8. 不会linux也没关系,点击鼠标即可完成的LDSC分析来了
  9. 《捉妖记》的命格解析
  10. THREE将左键改为平移右键改为旋转