Scala Ref/MVar的有效更新

Scala Ref/MVar的有效更新,scala,concurrency,functional-programming,scala-cats,Scala,Concurrency,Functional Programming,Scala Cats,我想对MVar或Ref中的值进行有效计算,并在计算成功时对其进行原子更新,或者在操作失败时返回初始值(如果MVar)/只需什么都不做(如果Ref) I.参考案例 val ref = Ref.of[IO, Int](0) def foo(i: Int): IO[Int] = //... some effectual computation 由于原子性很重要,Ref不提供compareAndSet操作,因此必须显式实现,这看起来不吸引人 II。MVar案例 val ref = Ref.of[IO

我想对
MVar
Ref
中的值进行有效计算,并在计算成功时对其进行原子更新,或者在操作失败时返回初始值(如果
MVar
)/只需什么都不做(如果
Ref

I.参考案例

val ref = Ref.of[IO, Int](0)

def foo(i: Int): IO[Int] = //... some effectual computation
由于原子性很重要,
Ref
不提供
compareAndSet
操作,因此必须显式实现,这看起来不吸引人

II。MVar案例

val ref = Ref.of[IO, Int](0)

def foo(i: Int): IO[Int] = //... some effectual computation
MVar提供互斥语义,但问题是
括号
不允许我们
放入计算值。以下是一个例子:

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 ()
您可以为此使用:

def updateAtomically(mvar: MVar[IO, Int], foo: Int => IO[Int]): IO[Int] =
  for {
    i  <- mvar.take
    ii <- foo(0).redeemWith(_ => IO(i), ii => mvar.put(ii) *> IO(ii))
  } yield ii
以及:


for(i@Martijn)会使mvar为空,从而导致死锁。“丑陋”是主观的。“漂亮”没有什么实际价值。你可以这样做,例如
mvar.take>=(i=>foo(i).trunt.map(u.fold(=>i,ii=>ii)).flatTap(mvar.put))
但它不会自动使代码变得更好。@MateuszKubuszok好吧,IMO丑陋会抑制可读性,所以美化它可能有一些价值…看起来更好,谢谢。我认为有必要实现恢复为
mvar.put(I)*>IO(I)
以避免空MVar。@SomeName是在
经济更新中更新
MVar
的唯一位置?如果是,请选择“是”。
import cats.Applicative.ops.toAllApplicativeOps
import cats.effect.{ ExitCode, IO, IOApp }
import cats.effect.concurrent.MVar

object Foo extends IOApp {

  def foo(i: Int): IO[Int] = IO(i + 1)
  def fooBar(i: Int): IO[Int] = IO.raiseError(new RuntimeException("BOOM"))

  def run(args: List[String]): IO[ExitCode] =
    (for {
      mvar <- MVar.of[IO, Int](0)
      res  <- updateAtomically(mvar, foo)
      _    <- IO(println(res))
    } yield res).map(_ => ExitCode.Success)
}
1
def run(args: List[String]): IO[ExitCode] =
  (for {
     mvar <- MVar.of[IO, Int](0)
     res  <- updateAtomically(mvar, fooBar)
     _    <- IO(println(res))
   } yield res).map(_ => ExitCode.Success)
0