英文:
Understanding cats-effect forceR definition?
问题
def forceR[B](that: IO[B]): IO[B] =
// 强制转换在这里是必要的,以欺骗编译器,避免产生 IO[Any]
asInstanceOf[IO[Unit]].handleError(_ => ()).productR(that)
- 自身的
asInstanceOf[IO[Unit]]
将自身转换为IO[Unit]
- 然后
handleError
的类型为def handleError[B >: A](f: Throwable => B): IO[B]
,通常情况下,返回的IO[B]
的类型约束应该是IO[Unit]
的超类型
那么为什么调用站点上的 forceR
定义具有真正的 IO[B]
类型,例如 IO[Int]
?
英文:
def forceR[B](that: IO[B]): IO[B] =
// cast is needed here to trick the compiler into avoiding the IO[Any]
asInstanceOf[IO[Unit]].handleError(_ => ()).productR(that)
- Self
asInstanceOf[IO[Unit]]
turns self into aIO[Unit]
- and then
handleError
has the typedef handleError[B >: A](f: Throwable => B): IO[B]
which normally stats the type bound of returningIO[B]
should be super type ofIO[Unit]
Then why this forceR
definition on the call site have the true IO[B]
type such as IO[Int]
?
答案1
得分: 3
Your question is slightly confusing.
> Then why this forceR
definition on the call site has the true IO[B]
type such as IO[Int]
?
Because the returned type of forceR
is specified to be IO[B]
. So it can't be anything else at the call site (if the code compiles).
I guess you mis-interprete the comment
// cast is needed here to trick the compiler into avoiding the IO[Any]
This is not to avoid return type IO[Any]
. The return type will anyway be IO[B]
because of productR
, @GaëlJ is correct
...
def productR[B](that: IO[B]): IO[B] =
flatMap(_ => that)
So this is not intended to make this.handleError(_ => ()).productR(that)
not IO[Any]
, this seems to be intended to make this.handleError(_ => ())
not IO[Any]
.
If this
is IO[A]
and A <: AnyVal
then this.handleError(_ => ())
has type IO[AnyVal]
. If not A <: AnyVal
then this.handleError(_ => ())
has type IO[Any]
. Not sure why it's important in handleError
though. Some reflection (.getClass
), caching, stack tracing is used in handleError
. Maybe error message is undesired for IO[Any]
.
handleErrorWith[B](t => IO.pure(f(t)))
def handleErrorWith[B >: A](f: Throwable => IO[B]): IO[B] =
IO.HandleErrorWith(this, f, Tracing.calculateTracingEvent(f))
def calculateTracingEvent(key: Any): TracingEvent = {
if (isCachedStackTracing) {
val cls = key.getClass
get(cls)
} else if (isFullStackTracing) {
buildEvent()
} else {
null
}
}
/**
* Holds platform-specific flags that control tracing behavior.
*
* <p>The Scala compiler inserts a volatile bitmap access for module field accesses. Because the
* `tracingMode` flag is read in various IO constructors, we are opting to define it in a Java
* source file to avoid the volatile access.
*
* <p>INTERNAL API with no source or binary compatibility guarantees.
*/
public final class TracingConstants {...}
Otherwise maybe casting was just a precaution and is not actually necessary
The specific commit where @DanielSpiewak
added the casting was here
The interesting question is why actually Daniel needed not IO[Any]
in the part before productR
and whether he actually needed that. I'm not 100% sure.
> @LuisMiguelMejíaSuárez:
>
> This is just speculation, but I am fairly sure is just to allow compilation in the presence of strict compiler flags. As you may know, there is a flag to make the compiler warn if it infers Any
, and another flag that turns all compiler warning into errors; thus stopping compilation. Additionally, as you may also be aware the sbt-tpolecat plugin enable those warnings and I am pretty sure most typelevel project either use that plugin, or enable a similar set of flags by other means. So, that is why they needed to avoid the inferred Any
while recovering from the possible errors.
Makes sense.
英文:
Your question is slightly confusing.
> Then why this forceR
definition on the call site have the true IO[B]
type such as IO[Int]
?
Because the returned type of forceR
is specified to be IO[B]
. So it can't be anything else at the call site (if the code compiles).
I guess you mis-interprete the comment
// cast is needed here to trick the compiler into avoiding the IO[Any]
This is not to avoid return type IO[Any]
. The return type will anyway be IO[B]
because of productR
, @GaëlJ is correct
sealed abstract class IO[+A] ... {
...
def productR[B](that: IO[B]): IO[B] =
flatMap(_ => that)
So this is not intended to make this.handleError(_ => ()).productR(that)
not IO[Any]
, this seems to be intended to make this.handleError(_ => ())
not IO[Any]
.
If this
is IO[A]
and A <: AnyVal
then this.handleError(_ => ())
has type IO[AnyVal]
. If not A <: AnyVal
then this.handleError(_ => ())
has type IO[Any]
. Not sure why it's important in handleError
though. Some reflection (.getClass
), caching, stack tracing is used in handleError
. Maybe error message is undesired for IO[Any]
.
def handleError[B >: A](f: Throwable => B): IO[B] =
handleErrorWith[B](t => IO.pure(f(t)))
def handleErrorWith[B >: A](f: Throwable => IO[B]): IO[B] =
IO.HandleErrorWith(this, f, Tracing.calculateTracingEvent(f))
def calculateTracingEvent(key: Any): TracingEvent = {
if (isCachedStackTracing) {
val cls = key.getClass
get(cls)
} else if (isFullStackTracing) {
buildEvent()
} else {
null
}
}
/**
* Holds platform-specific flags that control tracing behavior.
*
* <p>The Scala compiler inserts a volatile bitmap access for module field accesses. Because the
* `tracingMode` flag is read in various IO constructors, we are opting to define it in a Java
* source file to avoid the volatile access.
*
* <p>INTERNAL API with no source or binary compatibility guarantees.
*/
public final class TracingConstants {...}
Otherwise maybe casting was just a precaution and is not actually necessary
The specific commit where @DanielSpiewak
added the casting was here
The interesting question is why actually Daniel needed not IO[Any]
in the part before productR
and whether he actually needed that. I'm not 100% sure.
> @LuisMiguelMejíaSuárez:
>
> This is just speculation, but I am fairly sure is just to allow compilation in the presence of strict compiler flags. As you may know, there is a flag to make the compiler warn if it infers Any
, and another flag that turns all compiler warning into errors; thus stopping compilation. Additionally, as you may also be aware the sbt-tpolecat plugin enable those warnings and I am pretty sure most typelevel project either use that plugin, or enable a similar set of flags by other means. So, that is why they needed to avoid the inferred Any
while recovering from the possible errors.
Makes sense.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论