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
的超类型,即 Exception
或 Throwable
。您有 IOException
,这就是它无法正确解析的原因。
一个for-comprehension是flatMap
和map
的链。让我们一行一行地跟着箭头走。
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
相同。我们也可以提供一个有用的值。
在我们研究flatMap
和map
在ZIO中是如何实现的之前,让我们先简单地看一下ZIO类型。
ZIO[R, E, A]
ZIO 效果取决于环境 R
,可能失败并返回错误 E
或成功并返回值 A
。 A
在您的示例中是最重要的,所以让我们专注于此。
putStrLn("What is your Name? ")
可以用值 Unit
成功。预计 after-all 打印到控制台不会 return 任何有意义的值。另一方面,getName
以 String
成功。成功使用来自控制台的输入导致 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
可能难以阅读。 *>
在这里是一个很好的选择,还有更多的组合器适用于不同的场景。
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
的超类型,即 Exception
或 Throwable
。您有 IOException
,这就是它无法正确解析的原因。
一个for-comprehension是flatMap
和map
的链。让我们一行一行地跟着箭头走。
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
相同。我们也可以提供一个有用的值。
在我们研究flatMap
和map
在ZIO中是如何实现的之前,让我们先简单地看一下ZIO类型。
ZIO[R, E, A]
ZIO 效果取决于环境 R
,可能失败并返回错误 E
或成功并返回值 A
。 A
在您的示例中是最重要的,所以让我们专注于此。
putStrLn("What is your Name? ")
可以用值 Unit
成功。预计 after-all 打印到控制台不会 return 任何有意义的值。另一方面,getName
以 String
成功。成功使用来自控制台的输入导致 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
可能难以阅读。 *>
在这里是一个很好的选择,还有更多的组合器适用于不同的场景。