Convert Future instances to cats-effect IO

Much (most?) of the third-parties software that you will have to deal with will not handle asynchronous actions using our beloved IO monad, they will instead use either Scala’s or Java’s Future. This brief entry shows how to convert those Futures into IO instances.

  • Scala Future to IO: actually this is pretty straightforward, just use IO.fromFuture:

      import cats.effect.IO
      import scala.concurrent.IO
    
      IO.fromFuture(
        IO(Future.successful("Scala Future converted to CE IO successfully"))
      )
    
  • Java CompletableFuture to IO: as easy as running IO.fromCompletableFuture:

      import cats.effect.IO
      import java.util.concurrent.CompletableFuture
    
      IO.fromCompletableFuture(
        IO(CompletableFuture.completedFuture("Java CompletableFuture converted to CE IO successfully"))
      )
    
  • Java Future to IO, this is unfortunately more convoluted. There is no a straightforward method to do this. Instead we will create a new method that periodically checks if the Future instance is completed and if so it returns its value, otherwise it sleeps (waits) and retries again. Note that the sleep call only blocks the fiber 'semantically', that is, the actual thread is not blocked. So this is a cheap method to execute. This method is a simplified version of this gist from Gavin Bisesi (Daenyth), which I encourage you to take a look. This is how we can define and use the method:

      import cats.effect.IO
    
      import java.util.concurrent.{CompletableFuture, Future => JFuture}
    
      // Convert a Java Future into an IO
      def convertToIO[A](fa: => JFuture[A]): IO[A] =
        IO.delay(fa.isDone).flatMap: isDone =>
          if (isDone) IO.delay(fa.get) // Future done, return the result
          else IO.sleep(5.milliseconds) >> convertToIO(fa) // Retry again in a little while
    
      // Dummy creation of a dummy Java Future
      val createJavaFuture: IO[JFuture[String]] =
         IO{
           val cf = new CompletableFuture[String]()
           cf.complete("Java Future converted to CE IO successfully")
           cf
         }
    
      createJavaFuture >>= convertToIO
    

So, that's all!

All code in this blog entry can be found in this gist. You can run it using scala-cli:

$ scala-cli FutureToIO.scala

Or by invoking the gist itself from scala-cli:

$ scala-cli https://gist.github.com/lrodero/4ae172a75b8a8d5bf72cba1890e3ac5f
1
Subscribe to my newsletter

Read articles from Luis Rodero-Merino directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Luis Rodero-Merino
Luis Rodero-Merino

Dev interested in functional programming solutions for Scala.