Search code examples
scalafunctional-programmingscala-cats

Understanding IO monad


I'm trying to understand asynchronous computation by cats.effect.IO examples and got some misunderstanding. The unsafe method unsafeRunAsync seems like running the underlying effect asynchronously (I expected that some ContextShift to be provided). But the method looks simply like this:

final def unsafeRunAsync(cb: Either[Throwable, A] => Unit): Unit =
  IORunLoop.start(this, cb)

Neither ContextShift nor ExecutionContext is provided. Here is the very simple example:

object TestIo extends App {
  println(s"Main thread = ${Thread.currentThread().getName}")
  val io = IO {
    println(s"Effect thread = ${Thread.currentThread().getName}")
    Thread.sleep(1000)
  }
  io.unsafeRunAsync(_ => println(s"Callback thread = ${Thread.currentThread().getName}"))
  println(s"Finished")
}

The output is

Main thread = main
Effect thread = main
Callback thread = main
Finished

As you can see everything here is run in the main thread synchronously. Can you please explain unsafeRunAsync? To me it seems the same as unsafeRunSync.


Solution

  • About

    Neither ContextShift nor ExecutionContext is provided.

    There is a context switch in cats.effect.IO

    See this blog: https://www.jaspervanzandbeek.com/scala/cats-io-monad/

    Here an example from there:

    def blockingOperation(): String = {
      // Read from file
      ...
    }
    
    val result: IO[String] = for {
      _ <- IO.shift(executionContextForBlockingOperations)
      result <- IO { blockingOperation() }
      _ <- IO.shift(mainExecutionContext)
    } yield result