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

ZIO标准库的核心数据类型是ZIO[R, E, A],这种类型的值被称为函数式作用

函数式作用是并发工作流的一种蓝图,如图1所示。该蓝图本质上是纯描述性的,必须执行才能观察到任何副作用,例如与数据库的交互,日志记录,流传输 网络中的数据,或接受请求。

ZIO [R,E,A]类型的函数式作用要求您在运行的时候提供R类型的值,并且在执行效果时,它可能会执行失败返回类型为E(错误类型),或者执行成功返回类型为A(成功类型)。

稍后我们将详细讨论这些类型参数。但是首先,我们需要了解将效果变为蓝图意味着什么。
在传统的过程编程中,我们习惯于代码的每一行直接与外界交互。例如,考虑以下代码片段:

val goShopping: Unit = {
println("Going to the grocery store")
}

scala程序一旦运行goShopping变量,就会立刻输出 Going to the grocery store. 这种风格的编程方式是过程式编程。基本上所有程序员都熟悉这种编程方式,因为大多数编程语言天生就是过程式的。
对于简单的程序而言,过程式编程是非常方便的。但是当我们这样写程序的时候,我们想要做的事情(去商店)与我们想要做的事情(现在去商店)纠缠不清。这种纠缠可能导致许多难以理解和测试的样板代码,难以更改,并且充满了直到生产才发现的细微错误。
例如,假设我们实际上不想现在去杂货店,而是从现在开始一个小时。我们可能会这样去编写代码:

ScheduledExecutorService:

import java.util.concurrent.{ Executors, ScheduledExecutorService } 
import java.util.concurrent.TimeUnit._

val scheduler: ScheduledExecutorService = Executors.newScheduledThreadPool(1)

scheduler.schedule(
	new Runnable { def run: Unit = goShopping }, 
	1,
	HOURS
) 

scheduler.shutdown()

在这段程序中我们创建了一个executor,在一小时后执行我们的 goShopping 操作。然后在程序执行完之后 shut down 调度器。
该解决方案不仅涉及难以理解,测试和难以更改的样板代码,而且还存在一个细微的错误!
因为goShopping是直接执行的,而不是工作计划,所以一旦JVM加载 goShopping,就会将 “Going to the grocery store”打印到控制台。因此,我们现在要去杂货店,而不是现在的一个小时!

实际上,我们计划在一小时内执行的唯一操作就是返回goShopping的Unit值,这根本不需要执行任何操作。

在这种情况下,我们可以通过将goShopping定义为def而不是val来推迟其计算,从而解决问题。但是这种方法脆弱且容易出错,迫使我们仔细考虑何时对程序中的每个语句进行求值,而不再是语句的顺序。

我们还必须注意不要过早计算。我们可能将其赋值或放入数据结构中,这可能会导致过早计算。好像我们谈购物一样,但是一旦提到“食品”一词,它们就已经在门口了!实际上我们只是打算去购买而已。

解决此问题(以及并发编程中的大多数问题)的方法是在程序值中创建描述我们想要做什么的语句。这样,我们就可以将我们想做的事情与想怎么去做这件事情分开。

下面这段代码展示了怎么用zio编写这段程序:

import zio._
val goShopping = ZIO.effect(println("Going to the grocery store"))

在这段程序中我们用 effect 的构造函数创建了一个 goShopping 的函数式作用。这个 effect 只是描述了我们要去超时,但是事实上没有立刻做任何事。这主要是因为 effect 的参数是 effect :=> A, 这是 () => A 的简写。可以在 scala REPL中测试这段代码,你并不会得到任何输出。

为了去超市,我们必须运行这个执行和定义分开的作用。从而使我们可以消除上面提到的问题,并极大地简化代码。

通过这种定义 go shopping的方式,我们现在可以阐述 在怎么把 ”how“ 和 “what” 分开,然后通过组合作用的方式解决复杂业务问题。

下面的示例通过 ZIO类型的静态方法 delay ,将 goShopping函数转换为一个一小时后执行的副作用。

import zio.clock._ 
import zio.duration._
val goShoppingLater = goShopping.delay(1.hour)

得益于可以将工作流描述为普通的不可变值功能,我们不必担心怎么去定义goShopping又或者过早的运行他。同样,delay 运算符返回的值只是另一个effect,因此我们可以轻松地使用它以相同的方式构建更复杂的程序。

在ZIO中,每个ZIO效果都只是一种描述-并发工作流程的蓝图。在编写程序时,我们创建了更大,更复杂的蓝图,这些蓝图更接近于解决业务问题。当我们完成并具有描述我们需要做的所有事情的效果时,我们将其交给ZIO runtime,ZIO runtime将执行该蓝图并产生程序结果。

那么我们如何实际运行ZIO效果?最简单的方法是扩展App特性并实现run方法,如以下代码片段所示:

import zio._
object GroceryStore extends App { 
	def run(args: List[String]) = goShopping.exitCode 
}

run函数要求我们返回一个ExitCode,该退出代码描述了进程终止时要使用的退出值。 在上定义的exitCode方法是一种方便的方法,可将所有成功都转换为ExitCode(0),将所有失败转换为ExitCode(1)。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值