Question: Is it safe to suspend unsafeRunSync
with IO
? E.g.
val io: IO[Unit] = //...
val io2: IO[Unit] = IO(io.unsafeRunSync)
The reason I would do so is that I have some class parameterized with F[_]: Effect
which is like a cache:
import cats.effect.Effect
final class MyChache[F[_]](implicit F: Effect[F]) {
private val cache = new ConcurrentHashMap[Int, String]
def getOrCreate(key: Int): F[String] = F delay {
cache.computeIfAbsent(
key,
k => longRunningEffecfulComputation(k).toIO.unsafeRunSync() // <-- Here
)
}
}
object MyCache {
def longRunningEffecfulComputation[F[_] : Effect](key: Int): F[String] = {
//...
}
}
The point is I want to run this long running effectfull computation only once for each key (it's pretty infrequent). Yet I would like to stay non-blocking when retrieving existing key.
ConcurrentHashMap
seems to be a perfect choice, but it requires this ugly trick with running and suspending the effect. Is there a better way to go?
This is at least potentially unsafe. Suppose that your long running computation used a fixed-size thread pool:
import java.util.concurrent.Executors
import scala.concurrent.ExecutionContext
import cats.effect.Async
object MyCache {
val smallThreadPool = ExecutionContext.fromExecutor(Executors.newFixedThreadPool(1))
def longRunningEffectfulComputation[F[_] : Effect](key: Int): F[String] = {
Effect[F].flatMap(Async.shift[F](smallThreadPool))(_ => Effect[F].delay("test"))
}
}
And your cache was used on the same thread pool:
val io = for {
_ <- IO.shift(MyCache.smallThreadPool)
x <- new MyCache[IO].getOrCreate(1)
} yield x
When you call io.unsafeRunSync()
, you will see that it does not terminate.
Instead, you can use a cache api that supports cats-effect, like ScalaCache.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With