Ref/MVar 的有效更新

Som*_*ame 6 concurrency functional-programming scala scala-cats

我想对MVaror内部的值应用有效计算Ref,并在计算成功时自动更新它,或者在操作失败时放回初始值(在 的情况下MVar)/简单地不执行任何操作(在 的情况下)。Ref

I. 参考案例

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

def foo(i: Int): IO[Int] = //... some effectual computation
Run Code Online (Sandbox Code Playgroud)

由于原子性很重要并且不幸的Ref是不提供compareAndSet操作,因此必须显式地实现它,这看起来并不吸引人。

二. MVar案例

MVar 提供了互斥语义,但问题是bracket不允许我们put计算值。这是一个例子:

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 ()
Run Code Online (Sandbox Code Playgroud)

有没有办法至少对MVaror实现这种行为Ref

更新

我用 实现了它MVar,但它看起来相当难看:

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
Run Code Online (Sandbox Code Playgroud)

Yuv*_*kov 4

您可以MonadError.redeemWith为此使用:

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
Run Code Online (Sandbox Code Playgroud)

进而:

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)
}
Run Code Online (Sandbox Code Playgroud)

产量:

1
Run Code Online (Sandbox Code Playgroud)

和:

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)
Run Code Online (Sandbox Code Playgroud)

产量:

0
Run Code Online (Sandbox Code Playgroud)