'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 ()
}
The version using flatMap is successfully working but when I try to run the for comprehension version then I am getting this error
Pattern guards are only supported when the error type is a supertype of NoSuchElementException. However, your effect has java.io.IOException for the error type. name: String <- getName
Solution 1:[1]
You need to remove type ascription from your name argument. Type ascription has special meaning in for comprehensions. By saying
for
name: String <- getStrLn
it translates to is something like getStrLn.withFilter(_.isInstanceOf[String])
withFilter
is provided by ZIO implicit but only if Error channel allows to signal about missing elements. Basically your E
should be supertype of NoSuchElementException
, i.e. Exception
or Throwable
. You have IOException
and that's why it's not resolving properly.
Solution 2:[2]
A for-comprehension is a chain of flatMap
and map
. Let's go line by line and follow the arrows.
for {
_ <- putStrLn("What is your Name? ")
name <- getName
_ <- putStrLn(s"Hello $name")
} yield ()
Here is what it would look like in a flatMap
.
Note that the flow is top to bottom, right to left (follow the arrows). The arrow point towards the the parameter in the anonymous function that you put inside the next flatMap
.
Also note that underscore _
means we discard the parameter.
putStrLn("What is your Name? ")
.flatMap { _ => getName
.flatMap { name => putStrLn(s"Hello $name")
.map(_ => Unit)
}
}
A for-comprehension ends with a yield. That is the map
inside the nested flatMap
. Note that ()
is the same as Unit
. We could also a useful value.
Before we look into how flatMap
and map
is implemented in ZIO, let's first look briefly at the ZIO type.
ZIO[R, E, A]
A ZIO effect depends on an enviroment R
and can either fail with an error E
or succeed with a value A
. A
is the most important in your example, so let's focus on that.
putStrLn("What is your Name? ")
can succeed with a value Unit
. It is expected after-all that printing to the console would not return any meaningful value. getName
on the other hand, succeeds with a String
. It makes sense that successfully getting using input from the console results in a String
.
So how does the flatMap
work?
def flatMap[R1 <: R, E1 >: E, B](k: (A) ? ZIO[R1, E1, B])(implicit trace: Trace): ZIO[R1, E1, B]
From ZIO[R, E, A]
we must give a function (A) ? ZIO[R1, E1, B]
. It means we take the success value of the previous ZIO effect and turn it into a new ZIO effect. Sometimes we care about A
will give our transforming function a named parameter like:
putStrLn("What is your Name? ").flatMap(name => putStrLn(s"Hello $name")
.
Sometimes we don't care about A
and we discard it:
putStrLn("What is your Name? ").flatMap(_ => getName /* code */ )
Finally there is the map
def map[B](f: (A) ? B)(implicit trace: Trace): ZIO[R, E, B]
At the end of our for-comprehension, we have to decide what to yield. It is optional to use the yield. Here is another example where we yield something useful:
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)
As a final note, you used *>
, also called zipRight
in the first code snippet.
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.
It is quite common to values when chaining effects, like when prompting a user for input. Too many nested flatMap
s can be hard to read. *>
is a good alternative here and there are many more combinators for different scenarios.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | SimY4 |
Solution 2 | Alexander Wiklund |