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)
asInstanceOf[IO[Unit]]
turns self into a IO[Unit]
handleError
has the type def handleError[B >: A](f: Throwable => B): IO[B]
which normally stats the type bound of returning IO[B]
should be super type of IO[Unit]
Then why this forceR
definition on the call site have the true IO[B]
type such as IO[Int]
?
Your question is slightly confusing.
Then why this
forceR
definition on the call site have the trueIO[B]
type such asIO[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.
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 inferredAny
while recovering from the possible errors.
Makes sense.