ZIO 中的 getStrLn 使用 flatMap 但不在内部进行理解

getStrLn in ZIO working with flatMap but not inside for comprehension

val askNameFlatMap: ZIO[Console, IOException, Unit] = putStrLn("What is your Name? ") *>
    getStrLn.flatMap(name => putStrLn(s"Hello $name"))

val askNameFor: ZIO[Console, IOException, Unit] = {
    val getName: ZIO[Console, IOException, String] = getStrLn
    for {
      _ <- putStrLn("What is your Name? ")
      name: String <- getName
      _ <- putStrLn(s"Hello $name")
    } yield ()
  }

使用 flatMap 的版本成功运行,但是当我尝试 运行 for comprehension 版本时出现此错误

仅当错误类型是 NoSuchElementException 的超类型时才支持模式守卫。但是,您的效果的错误类型为 java.io.IOException。 名称:字符串 <- getName

您需要从名称参数中删除类型归属。类型归属对于理解有特殊的意义。通过说

  for
    name: String <- getStrLn

它翻译成类似于 getStrLn.withFilter(_.isInstanceOf[String])

withFilter 由 ZIO 隐式提供,但前提是错误通道允许发出有关丢失元素的信号。基本上你的 E 应该是 NoSuchElementException 的超类型,即 ExceptionThrowable。您有 IOException,这就是它无法正确解析的原因。

一个for-comprehension是flatMapmap的链。让我们一行一行地跟着箭头走。

for {
  _ <- putStrLn("What is your Name? ")
  name <- getName
  _ <- putStrLn(s"Hello $name")
} yield ()

这是 flatMap.

中的样子

请注意,流程是从上到下,从右到左(按照箭头)。箭头指向您放在下一个 flatMap.

中的匿名函数中的参数

另请注意,下划线 _ 表示我们丢弃该参数。

putStrLn("What is your Name? ")
  .flatMap { _ => getName
    .flatMap { name => putStrLn(s"Hello $name")
      .map(_ => Unit)
    }
  } 

A for-comprehension 以 yield 结束。即嵌套flatMap里面的map。请注意 ()Unit 相同。我们也可以提供一个有用的值。

在我们研究flatMapmap在ZIO中是如何实现的之前,让我们先简单地看一下ZIO类型。

ZIO[R, E, A]

ZIO 效果取决于环境 R,可能失败并返回错误 E 或成功并返回值 AA 在您的示例中是最重要的,所以让我们专注于此。

putStrLn("What is your Name? ") 可以用值 Unit 成功。预计 after-all 打印到控制台不会 return 任何有意义的值。另一方面,getNameString 成功。成功使用来自控制台的输入导致 String.

是有道理的

那么 flatMap 是如何工作的?

def flatMap[R1 <: R, E1 >: E, B](k: (A) ⇒ ZIO[R1, E1, B])(implicit trace: Trace): ZIO[R1, E1, B]

ZIO[R, E, A]我们必须给出一个函数(A) ⇒ ZIO[R1, E1, B]。这意味着我们将以前的 ZIO 效果的成功值转换为新的 ZIO 效果。有时我们关心 A 会给我们的转换函数一个命名参数,如:

putStrLn("What is your Name? ").flatMap(name => putStrLn(s"Hello $name").

有时我们不关心A而丢弃它:

putStrLn("What is your Name? ").flatMap(_ => getName /* code */ )

终于有map

def map[B](f: (A) ⇒ B)(implicit trace: Trace): ZIO[R, E, B]

在我们的 for-comprehension 结束时,我们必须决定产出什么。使用 yield 是可选的。这是另一个例子,我们产生了一些有用的东西:

case class Person(firstName: String, lastName: String)

val signUpForm = for {
  _ <- putStrLn("What is your first name? ")
  firstName <- getStrLn
  _ <- putStrLn(s"What is your last name? ")
  lastName <- getStrLn
} yield Person(firstName, lastName)

最后一点,您在第一个代码片段中使用了 *>,也称为 zipRight

final def *>[R1 <: R, E1 >: E, B](that: ⇒ ZIO[R1, E1, B])(implicit trace: Trace): ZIO[R1, E1, B]

A variant of flatMap that ignores the value produced by this effect.

链接效果时值很常见,例如提示用户输入时。太多嵌套的 flatMap 可能难以阅读。 *> 在这里是一个很好的选择,还有更多的组合器适用于不同的场景。