Skip to content

Instantly share code, notes, and snippets.

@hilios
Last active April 2, 2019 14:44
Show Gist options
  • Save hilios/8f749a1e9b82b15b8ca2fb6abeb0bfe2 to your computer and use it in GitHub Desktop.
Save hilios/8f749a1e9b82b15b8ca2fb6abeb0bfe2 to your computer and use it in GitHub Desktop.
Effect[Future]

Instances of cats Effect for Future

Future is not a Monad we all know that, but sometimes we have Java developers programming in Scala. This implementation can help the effort to migrate legacy code to Tagless Final.

implicit def futureEffect: Effect[Future] = FutureEffect()

implicit def futureEffect: ConcurrentEffect[Future] = FutureConcurrentEffect()
import scala.concurrent.{Await, ExecutionContext, Future, Promise}
import cats.implicits._
import scala.concurrent.duration.Duration
import scala.util.{Failure, Success}
final case class FutureConcurrentEffect()(implicit ec: ExecutionContext) extends FutureEffect with ConcurrentEffect[Future] {
def start[A](fa: Future[A]): Future[Fiber[Future, A]] = Future.successful {
FutureFiber(fa)
}
def racePair[A, B](fa: Future[A], fb: Future[B]): Future[Either[(A, Fiber[Future, B]), (Fiber[Future, A], B)]] = ???
def runCancelable[A](fa: Future[A])(cb: Either[Throwable, A] => IO[Unit]): SyncIO[CancelToken[Future]] = ???
}
import scala.concurrent.{Await, ExecutionContext, Future, Promise}
import cats.implicits._
import scala.concurrent.duration.Duration
import scala.util.{Failure, Success}
final case class FutureEffect()(implicit ec: ExecutionContext) extends Effect[Future] with ConcurrentEffect[Future] {
def runAsync[A](fa: Future[A])(cb: Either[Throwable, A] => IO[Unit]): SyncIO[Unit] = SyncIO {
fa.onComplete(_.fold(fa => cb(Left(fa)), fb => cb(Right(fb))))
}
def async[A](k: (Either[Throwable, A] => Unit) => Unit): Future[A] = {
val p = Promise[A]()
k(_.fold(p.failure, p.success))
p.future
}
def asyncF[A](k: (Either[Throwable, A] => Unit) => Future[Unit]): Future[A] = {
val p = Promise[A]()
k(_.fold(p.failure, p.success))
p.future
}
def suspend[A](thunk: => Future[A]): Future[A] =
Future.successful(()).flatMap(_ => thunk)
def bracketCase[A, B](acquire: Future[A])
(use: A => Future[B])
(release: (A, ExitCase[Throwable]) => Future[Unit]): Future[B] = for {
a <- acquire
etb <- use(a).transformWith[Either[Throwable, B]] {
case Success(v) => pure(Right(v))
case Failure(e) => pure(Left(e))
}
_ <- release(a, etb match {
case Right(_) => ExitCase.complete
case Left(t) => ExitCase.error(t)
})
e <- etb.fold(raiseError, pure)
} yield e
def pure[A](x: A): Future[A] = Future.successful(x)
def flatMap[A, B](fa: Future[A])(f: A => Future[B]): Future[B] = fa.flatMap(f)
def tailRecM[A, B](a: A)(f: A => Future[Either[A, B]]): Future[B] = flatMap(f(a)) {
case Left(a) => tailRecM(a)(f)
case Right(b) => pure(b)
}
def raiseError[A](e: Throwable): Future[A] = Future.failed(e)
def handleErrorWith[A](fa: Future[A])(f: Throwable => Future[A]): Future[A] = fa.recoverWith {
case e: Throwable => f(e)
}
}
import scala.concurrent.{Await, ExecutionContext, Future, Promise}
import cats.implicits._
import scala.concurrent.duration.Duration
import scala.util.{Failure, Success}
final case class FutureFiber[A](fa: Future[A])(implicit ec: ExecutionContext) extends Fiber[Future, A] {
val p = Promise[A]()
fa.onComplete(_.fold(p.failure, p.success))
def cancel: CancelToken[Future] = Future.successful {
p.failure(new InterruptedException())
()
}
def join: Future[A] = Await.ready(p.future, Duration.Inf)
}
@nightscape
Copy link

Hi @hilios, thanks for this code snippet!
I fixed some compile errors here in case you want to incorporate them:
https://gist.github.com/nightscape/03910b948e3197a296b7b9f295dc5df8

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment