如何让 Scalaz ZIO 变懒?

2024-02-28

我有一个严重的副作用函数(想想数据库调用),我想将其用作惰性值,以便仅在第一次使用时调用它(如果从未使用过则根本不会调用)。

我该如何使用 ZIO 做到这一点?

如果我的程序如下所示,则该函数仅被调用一次(但甚至根本不使用结果):

import scalaz.zio.IO
import scalaz.zio.console._

object Main extends scalaz.zio.App {

  def longRunningDbAction: IO[Nothing, Integer] = for {
    _ <- putStrLn("Calling the database now")
  } yield 42

  def maybeUseTheValue(x: Integer): IO[Nothing, Unit] = for {
    _ <- putStrLn(s"The database said ${x}")
  } yield ()

  def maybeNeedItAgain(x: Integer): IO[Nothing, Unit] = for {
    _ <- putStrLn("Okay, we did not need it again here.")
  } yield ()

 override def run(args: List[String]): IO[Nothing, Main.ExitStatus] = for {
    valueFromDb <- longRunningDbAction
    _ <- maybeUseTheValue(valueFromDb)
    _ <- maybeNeedItAgain(valueFromDb)
  } yield ExitStatus.ExitNow(0)

}

我想我必须通过IO产生的Int而不是已经具体化的Int,但是如果我传递原始的IO只是调用数据库,它会被重复调用:

object Main extends scalaz.zio.App {

  def longRunningDbAction: IO[Nothing, Integer] = for {
    _ <- putStrLn("Calling the database now")
  } yield 42


  def maybeUseTheValue(x: IO[Nothing, Integer]): IO[Nothing, Unit] = for {
    gettingItNow <- x
    _ <- putStrLn(s"The database said ${gettingItNow}")
  } yield ()

  def maybeNeedItAgain(x: IO[Nothing, Integer]): IO[Nothing, Unit] = for {
    gettingItNow <- x
    _ <- putStrLn(s"Okay, we need it again here: ${gettingItNow}")
  } yield ()

  override def run(args: List[String]): IO[Nothing, Main.ExitStatus] = for {
    _ <- maybeUseTheValue(longRunningDbAction)
    _ <- maybeNeedItAgain(longRunningDbAction)
  } yield ExitStatus.ExitNow(0)

}

有没有办法“包裹”longRunningDbAction去做一些让它变得懒惰的事情?


我想出了以下几点:

 def lazyIO[E,A](io: IO[E,A]): IO[Nothing, IO[E, A]] = {
    for {
      barrier <- Promise.make[Nothing, Unit]
      fiber <- (barrier.get *> io).fork
    } yield barrier.complete(()) *> putStrLn("getting it") *> fiber.join
  }

ZIO 1.0-RC4 的更新版本(具有环境支持):

def lazyIO[R, E, A](io: ZIO[R, E, A]): ZIO[R, Nothing, ZIO[R, E, A]] = {
  for {
    barrier <- Promise.make[Nothing, Unit]
    fiber <- (barrier.await *> io).fork
  } yield barrier.succeed(()) *> fiber.join
}

所以这是一个接受 IO 并返回它的惰性版本的 IO。

它的工作原理是启动一个fiber运行原来的io,但只有在 Promise 之后(barrier) 已经完成。

惰性IO首先完成barrier(如果它是第一个这样做的人,将会解锁fiber依次运行包装的io),然后加入fiber检索计算结果。

有了这个,我可以做

override def run(args: List[String]): IO[Nothing, Main.ExitStatus] = for {
    valueFromDb <- lazyIO(longRunningDbAction)
    _ <- maybeUseTheValue(valueFromDb)
    _ <- maybeNeedItAgain(valueFromDb)
  } yield ExitStatus.ExitNow(0)

控制台输出显示,惰性值确实被拉取两次,但只有第一次触发“数据库访问”:

getting it
Calling the database now
The database said 42
getting it
Okay, we need it again here: 42
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何让 Scalaz ZIO 变懒? 的相关文章

随机推荐

  • HTML5 音频的 ontimeupdate 在 Chrome 中未触发

    我有以下代码 用于创建 html5 音频元素并设置时间更新时的侦听器 音频在所有浏览器中都能正常播放 但 ontimeupdate 函数在 Chrome 中永远不会触发 包括 Android 上的 Chrome var audioEleme
  • 为什么在 Kotlin 中异步设置视图尺寸不起作用?

    这有效 myView layoutParams myView layoutParams apply height 100 但这都不是 使用 android ktx myView doOnLayout myView layoutParams
  • 无法在桌面应用程序背后的代码中绑定网格视图

    我有一个列出所有客户的网格视图 我将它绑定在放置在 MDI 子项中的 Form 的加载时间中 网格视图中的列是在设计时预定义的 我的代码Form Load 事件是 try cn db createConnection if cn State
  • ViewModel 没有零参数构造函数错误 - 即使它有零参数构造函数

    我是 Android 和 Java 新手 正在尝试制作一个基于位置的应用程序 EDIT 我做了一个非常非常简单的测试代码 但得到了同样的错误 这是java package com example viewmodeltest import a
  • 从电子邮件中的链接启动 iPhone 应用程序

    我一直在尝试 iPhone SDK 中的 URL 方案 并且我已经使用自定义 URL 方案 例如 myap Dosomething 来启动我的应用程序 但这对于我想要的东西来说并不实际 是否可以以某种方式注册一个方案 允许电子邮件中的链接
  • 与assert_select相反?

    我正在编写一个应用程序 需要检查视图是否存在not具有某些功能 特别是因为该功能必须仅呈现给特定安全组中的用户 我正在寻找与assert selects相反的内容 以便看到菜单是not呈现 看看这里的文档 http apidock com
  • java优先级队列与链表的比较

    我正在解决BFS问题 我使用了 PriorityQueue 但我得到了错误的答案 然后我使用了LinkedList 我猜对了并且 我无法找到它们之间的区别 这是两个代码 为什么两个答案不同 Code1 LinkedList q new Li
  • 如何创建一个 React Native ios 共享扩展应用程序

    我希望我的反应本机应用程序可以从 Whatsapp Skype 照片共享 我尝试使用反应本机共享扩展 https www npmjs com package react native share extension但它只在 Safari 浏
  • watchOS 2.2 应用程序如何确定其配对的 iPhone 是否已切换到另一台 Apple Watch?

    我正在尝试在我的 iOS 9 3 watchOS 2 2 应用程序中支持与多个手表配对的新功能 它似乎运行良好 只是我无法弄清楚 watchOS 应用程序如何确定配对的 iPhone 是否已切换到另一台 Apple Watch The do
  • 从 NSBundle 获取 nil 路径

    我在项目中创建了一个新文件夹 在其中复制了图像 称为 io jpg 我还检查了构建阶段 gt 复制捆绑资源 文件就在那里 所以我试图获取这张图片的路径 NSBundle bundle NSBundle mainBundle NSString
  • ActiveMQ如何处理关闭的会话

    我正在使用 ActiveMQ 将电子邮件消息排入队列 消费者读取队列并发送电子邮件 在启动时 我注册一个生产者并永久缓存它 PooledConnectionFactory factory new PooledConnectionFactor
  • 词法或处理器问题:未找到 boost/config/user.hpp' 文件

    当我在 Xcode 中运行 React Native 应用程序时 它显示错误 boost config user hpp 文件未找到 而且当我使用命令 react native run ios 运行应用程序时 我在终端中收到错误 Comma
  • gdax-java 作为库的实现

    我正在尝试实现这个API https github com robevansuk gdax java https github com robevansuk gdax java以便能够通过 Coinbase 创建订单 提取资金和存入资金并在
  • 在ANDROID中打开一个activity而不在manifest文件中声明它?

    我想打开一个活动而不在清单文件中声明它 我不知道这是否可能 我真正想要的是使用意图从我的程序中动态打开一个活动 如果可能的话 任何人都可以帮助我吗 不可能 虽然我不确定你的意思是 动态打开活动 See http developer andr
  • 覆盖 FOSUserBundle 路由 Symfony2

    我想覆盖 FOSUserBundle 的一些路由 MyBundle Resources config routing security yml fos user security login path locale login defaul
  • Spyder 内部问题,如何解决?

    我对编码完全陌生 但是 当我尝试运行如图所示的一些代码时 出现了这个问题 我尝试重新安装但不起作用 Spyder 维护者在这里 带来不便敬请谅解 这是 Anaconda 中的一个错误 我们已经向他们报告了该错误 但尚未修复 如果您只需要使用
  • 将 SVG 元素添加到 html 文件中?

    我想将一些 SVG 元素添加到 html 文件中 我发现链接说不支持此功能 文档类型必须是 xhtml 但我正在使用一些带有 html 的 javascript 库 这些库允许我在其中使用 SVG Raphael SVG Web 等 那么有
  • 在开发过程中禁用浏览器缓存 css 和 js 文件

    如何在应用程序开发过程中禁用浏览器缓存 以便每当我们对本地主机 或任何开发环境 中的 css 或 js 文件进行任何更改时 我们都不必在浏览器上进行硬刷新才能看到更改 对所有 css 和 js 都尝试这个 这个例子你应该在所有css和js中
  • Laravel 中间件单元测试

    我正在尝试在 Laravel 中为我的中间件编写单元测试 有谁知道教程 或者有这方面的例子吗 我已经写了很多代码 但是一定有更好的方法来测试handle方法 使用 Laravel 5 2 我通过向中间件传递带有输入的请求和带有断言的闭包来对
  • 如何让 Scalaz ZIO 变懒?

    我有一个严重的副作用函数 想想数据库调用 我想将其用作惰性值 以便仅在第一次使用时调用它 如果从未使用过则根本不会调用 我该如何使用 ZIO 做到这一点 如果我的程序如下所示 则该函数仅被调用一次 但甚至根本不使用结果 import sca