I'd like to apply effectual computation to the value inside MVar or Ref and atomically update it in case the computation succeeds or put back the initial value (in case of MVar)/simply do nothing(in case of Ref) in case the operation fails.
I. Ref-case
val ref = Ref.of[IO, Int](0)
def foo(i: Int): IO[Int] = //... some effectual computation
Since atomicity matters and Ref does not provide compareAndSet operation unfortunately so it must be implemnted explicitly which does not look attracting.
II. MVar-case
MVar provides mutually-exclusion semantic, but the problem is that bracket does not allow us to put the computed value. Here is an example:
val mvar = MVar.of[IO, Int](0)
def foo(i: Int): IO[Int] = IO(i + 1)
for {
mvar <- mvar
i <- mvar.take.bracket(foo)(mvar.put) //puts back 0, not 1
} yield ()
Is there a way to implement such behavior at least for either MVar or Ref?
UPD:
I implemented it with MVar, but it looks rather ugly:
def updateAtomically(mvar: MVar[IO, Int], foo: Int => IO[Int]): IO[Int] = for {
i <- mvar.take
ii <- foo(i).onError{
case t => mvar.put(i)
}
_ <- mvar.put(ii)
} yield ii