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?
computeIfAbsentanyway.ConcurrentHashMap[Int, F[String]]. If you go all through all the trouble of using cats-effect, strive to use it uniformly across the code base. Having an operation blow on you withunsafeRunSynccan be unexpected.ConcurrentHashMap[Int, F[String]]could be a solution if I did not have a requirements of computing the value only once per key. Problably, there can be some trick withConcurrentHashMap[Int, F[MVar[F, String]]], but I'm not sure... I think there is a design issue with all this code.ConcurrentHashMapI can be sure that the computation is done only once per key and when it is performed we have no other bucket locked. That was the actual reason behind usingConcurrentHashMap::computeIfAbsent. I could not find something more pure yet still practical.F[String], it should only be computed once assuming no one cheats withunsafeRunSync.