Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Eagerly-evaluate-and-forget behavior for Cats Effect IO

I'm converting Future code to IO. I have code similar to this

def doSomething: Future[Foo] = {
  Future { 
    //some code the result of which we don't care about 
  }
  Future {
    //Foo
  }
}

And then at the end of the program, I doSomething.unsafeRunSync. How do I convert these Futures to IOs while maintaining the fire-and-forget functionality of the first Future? In using IO's async API, I am worried about accidentally blocking the thread when I later call unsafeRunSync on doSomething.

like image 573
Lasf Avatar asked May 15 '18 16:05

Lasf


People also ask

What is Io in cats effect?

Companion object IO An IO is a data structure that represents just a description of a side effectful computation. IO can describe synchronous or asynchronous computations that: on evaluation yield exactly one result 2.

What is cats in Scala?

Cats is a library which provides abstractions for functional programming in the Scala programming language. Scala supports both object-oriented and functional programming, and this is reflected in the hybrid approach of the standard library.

What is fiber in Scala?

You can think of fibers as being lightweight threads, a fiber being a concurrency primitive for doing cooperative multi-tasking. trait Fiber[F[_], A] { def cancel: F[Unit] def join: F[A] } For example a Fiber value is the result of evaluating IO.start : import cats.effect.{Fiber, IO} import scala.concurrent.


1 Answers

A solution that uses only cats-effect could use IO.start. This, combined with the fact that you will then never join the resulting Fiber, will look something like this:

import cats.effect._
import cats.implicits._    
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._

object ExampleApp extends App{

  val fireAndForget =
    IO(println("Side effect pre-sleep")) *>
      IO.sleep(2.seconds) *>
      IO(println("Side effect post-sleep"))

  val toBeUsed = IO{
    println("Inside second one")
    42
  }

  val result = for {
    fiber <- IO.shift *> fireAndForget.start
    res <- toBeUsed.handleErrorWith { error =>
      // This is just in case you 'toBeUsed' can actually fail,
      // and you might want to cancel the original side-effecting IO
      fiber.cancel *> IO.raiseError(error) }
  } yield res

  println(result.unsafeRunSync())

  println("Waiting 3 seconds...")
  IO.sleep(3.seconds).unsafeRunSync()
  println("Done")
}

This will print (most of the times) something similar to:

Side effect pre-sleep 
Inside second one 
42                       // Up until here, will be printed right away
Waiting 3 seconds...     // It will then be waiting a while
Side effect post-sleep   // ...at which point the side effecting code terminates
Done 

Finally, here are more details about Fiber and IO.shift

like image 186
mdm Avatar answered Sep 27 '22 21:09

mdm