cps.monads.catsEffect.CatsAsync.scala Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of cps-async-connect-cats-effect_3 Show documentation
Show all versions of cps-async-connect-cats-effect_3 Show documentation
cps-async-connect: integration of dotty-cps-async with effect stacks
The newest version!
package cps.monads.catsEffect
import cps._
import cats.{Monad, MonadThrow}
import cats.effect.kernel._
import scala.util._
import scala.util.control.NonFatal
import scala.concurrent._
trait CatsMonad[F[_]](using mf: Monad[F]) extends CpsMonad[F]:
def pure[A](a:A): F[A] =
mf.pure(a)
def map[A,B](fa: F[A])(f: A => B): F[B] =
mf.map(fa)(f)
def flatMap[A,B](fa: F[A])(f: A => F[B]): F[B] =
mf.flatMap(fa)(f)
class CatsMonadPure[F[_]](using mf: Monad[F]) extends CatsMonad[F](using mf) with CpsPureMonadInstanceContext[F]
class CatsMonadThrow[F[_]](using MonadThrow[F]) extends CatsMonad[F] with CpsTryMonadInstanceContext[F]:
def error[A](e: Throwable): F[A] =
summon[MonadThrow[F]].raiseError(e)
override def mapTry[A,B](fa:F[A])(f: Try[A]=>B): F[B] =
summon[MonadThrow[F]].redeem(fa)( ex => f(Failure(ex)), a => f(Success(a)) )
def flatMapTry[A,B](fa:F[A])(f: Try[A]=>F[B]): F[B] =
summon[MonadThrow[F]].redeemWith(fa)( ex => f(Failure(ex)), a => f(Success(a)) )
class CatsMonadCancel[F[_]](using F: MonadCancel[F, Throwable]) extends CatsMonadThrow[F] with CpsTryMonadInstanceContext[F]:
// will be override when Sync become available
def poorMansDelay[A](a: =>A) = F.map(F.unit)(_ => a)
def poorMansFlatDelay[A](a: => F[A]) = F.flatMap(F.unit)(_ => a)
override def withAction[A](fa: F[A])(action: => Unit): F[A] = {
withAsyncAction(fa)(poorMansDelay(action))
}
override def withAsyncAction[A](fa: F[A])(action: => F[Unit]): F[A] = {
//F.guaranteeCase(fa)(_ => poorMansFlatDelay(action))
import cats.syntax.all.*
F.uncancelable { poll =>
F.onCancel(poll(fa), poorMansFlatDelay(action) ).handleErrorWith { ex =>
action.handleErrorWith { ex1 =>
ex1.addSuppressed(ex)
F.raiseError(ex1)
}.flatMap(_ => F.raiseError(ex))
}.flatTap { _ =>
action
}
}
}
override def withAsyncFinalizer[A](fa: => F[A])(f: => F[Unit]): F[A] = {
withAsyncAction(tryImpure(fa))(f)
}
object CatsAsyncHelper:
def adoptCallbackStyle[F[_],A](source: (Try[A]=>Unit) => Unit)(using Async[F]): F[A] = {
def adoptIOCallback(ioCallback: Either[Throwable, A] => Unit): Try[A] => Unit = {
case Failure(ex) => ioCallback(Left(ex))
case Success(a) => ioCallback(Right(a))
}
summon[Async[F]].async_ {
ioCallback => source(adoptIOCallback(ioCallback))
}
}
end CatsAsyncHelper
class CatsAsync[F[_]](using Async[F]) extends CatsMonadThrow[F] with CpsAsyncEffectMonadInstanceContext[F]:
def adoptCallbackStyle[A](source: (Try[A]=>Unit) => Unit): F[A] =
CatsAsyncHelper.adoptCallbackStyle[F,A](source)
end CatsAsync
class CatsAsyncCancel[F[_]](using Async[F], MonadCancel[F, Throwable]) extends CatsMonadCancel[F] with CpsAsyncEffectMonadInstanceContext[F]:
override def poorMansDelay[A](a: => A) = summon[Async[F]].delay(a)
override def poorMansFlatDelay[A](a: => F[A]) = summon[Async[F]].defer(a)
override def withAction[A](fa: F[A])(action: => Unit): F[A] = {
withAsyncAction(fa)(summon[Async[F]].delay(action))
}
override def withAsyncAction[A](fa: F[A])(action: => F[Unit]): F[A] = {
import cats.syntax.all.*
summon[MonadCancel[F, Throwable]].uncancelable { poll =>
summon[MonadCancel[F, Throwable]].onCancel(poll(fa), summon[Async[F]].defer(action))
.handleErrorWith { ex =>
action.handleErrorWith{ ex1 =>
ex1.addSuppressed(ex)
summon[MonadCancel[F,Throwable]].raiseError(ex1)
}.flatMap{ _ =>
summon[MonadCancel[F,Throwable]].raiseError(ex)
}
}
.flatTap { _ => action }
}
}
// inlined to avoid unoptimized virtual calls
//override def withAction[A](fa: F[A])(action: => Unit): F[A] = {
// summon[MonadCancel[F,Throwable]].guaranteeCase(fa) { _ => summon[Async[F]].delay(action) }
//}
// inlined to avoid unoptimized virtual calls
//override def withAsyncAction[A](fa: F[A])(action: => F[Unit]): F[A] = {
// summon[MonadCancel[F,Throwable]].guaranteeCase(fa)(_ => summon[Async[F]].defer(action))
//}
def adoptCallbackStyle[A](source: (Try[A]=>Unit) => Unit): F[A] =
CatsAsyncHelper.adoptCallbackStyle(source)
end CatsAsyncCancel
class CatsConcurrent[F[_]](using Concurrent[F], Async[F], MonadCancel[F, Throwable]) extends CatsAsyncCancel[F]
with CpsConcurrentEffectMonadInstanceContext[F]:
type Spawned[A] = Fiber[F,Throwable,A]
def spawnEffect[A](op: => F[A]): F[Spawned[A]] =
summon[Concurrent[F]].start{
try
op
catch
case NonFatal(ex) =>
error(ex)
}
def join[A](op: Spawned[A]): F[A] =
flatMap(op.join){ r =>
r match
case Outcome.Succeeded(fa) => fa
case Outcome.Errored(ex) => error(ex)
case Outcome.Canceled() => error(new CancellationException("fiber is cancelled"))
}
def tryCancel[A](op: Spawned[A]): F[Unit] =
op.cancel
given catsMonadPure[F[_]](using Monad[F], NotGiven[MonadThrow[F]]): CpsPureMonadInstanceContext[F] = CatsMonadPure[F]()
given catsMonadThrow[F[_]](using MonadThrow[F], NotGiven[Async[F]]): CpsTryMonadInstanceContext[F] = CatsMonadThrow[F]()
given catsMonadCancel[F[_]](using MonadCancel[F, Throwable], NotGiven[Async[F]]): CatsMonadCancel[F] = CatsMonadCancel[F]()
given catsAsync[F[_]](using Async[F], NotGiven[Concurrent[F]], NotGiven[MonadCancel[F,Throwable]]): CatsAsync[F] = CatsAsync[F]()
given catsConcurrent[F[_]](using Concurrent[F], Async[F], MonadCancel[F,Throwable]): CpsConcurrentEffectMonadInstanceContext[F] = CatsConcurrent[F]()
© 2015 - 2025 Weber Informatics LLC | Privacy Policy