@vimalaguti you want to use ConcurrentEffect
, like any other typeclasses, when you don't want to be tied to a concrete IO
in your signatures.
Specifically, Effect
is used when you need to interop with some library that calls YOUR code, such as APIs that let you register a callback (almost everything in Scala.JS) and Concurrent
is used where you want flow control with fibers and .start
, or for data structures with asynchronous blocking, like Semaphore
, Deferred
, fs2's Queue
, monix ConcurrentChannel
, etc.
ConcurrentEffect
bundles the two to avoid ambiguity issues (they both extend Async
which would break extension methods), and also gives you a run, but give me a cancel token
on top of Effect
.
IO
has some intricacies - you can't do io.start
without implicit ContextShift[IO]
in scope, but you can always unsafeRunAsync
it. For instance, Monix Task requires a Scheduler
for unsafeRunAsync
but you can always do task.start
. ConcurrentEffect
lets you write code that is independent of these details and works for all effect types in the same way.
It isn't, however, a typeclass you want to use often, it literally says "here be dragons, anything can happen", and also you can't use it with anything less powerful than IO+ContextShift, or a Task+Scheduler. Something that only requires Sync
, for example, can be used with SyncIO
or OptionT[IO, ?]
ConcurrentEffect
is a typeclass of last resort.
Async
, so that I can run many of them, and use Deferred
as data type?
Async
doesn't let you "run many of them" out of the box - if your underlying API is thread-blocking, you can't get around with thread management, but you should be able to only use Sync[F] + ContextShift[F] + Blocker
Concurrent
on the outside, to manage two slots independently, but that is irrelevant
Concurrent
is probably better since you get cancelable version of Deferred
when you have it
scala> :paste
// Entering paste mode (ctrl-D to finish)
import cats.effect._
import cats.implicits._
import cats.effect.implicits._
import scala.concurrent.ExecutionContext
val cs = IO.contextShift(ExecutionContext.global)
def parMapIO(implicit cs: ContextShift[IO]) = {
val ioA = IO("A")
val ioB = IO(10)
val ioC = IO(false)
(ioA, ioB, ioC).parMapN { (a, b, c) => println(s"done: $a $b $c") }
}
scala> parMapIO(cs).unsafeRunSync
done: A 10 false
scala> :paste
// Entering paste mode (ctrl-D to finish)
def parMapF[F[_]](implicit F: ConcurrentEffect[F], cs: ContextShift[F]) = {
val ioA = F.delay("A")
val ioB = F.delay(10)
val ioC = F.delay(false)
(ioA, ioB, ioC).parMapN { (a, b, c) => println(s"$a > $b > $c") }
}
// Exiting paste mode, now interpreting.
<pastie>:25: error: could not find implicit value for parameter p: cats.NonEmptyParallel[F,F]
(ioA, ioB, ioC).parMapN { (a, b, c) => println(s"$a > $b > $c") }
^
<pastie>:21: warning: parameter value F in method parMapF is never used
def parMapF[F[_]](implicit F: ConcurrentEffect[F], cs: ContextShift[F]) = {
^
<pastie>:21: warning: parameter value cs in method parMapF is never used
def parMapF[F[_]](implicit F: ConcurrentEffect[F], cs: ContextShift[F]) = {
^
Long.MaxValue
as the number of permits is a fine way of getting what I need. What are your thoughts?