【问题标题】:Effectful update of Ref/MVar有效更新 Ref/MVar
【发布时间】:2021-02-22 04:39:54
【问题描述】:

我想对 MVarRef 中的值应用有效的计算,并原子地更新它以防计算成功或放回初始值(如果是 @987654323 @)/干脆什么都不做(Ref),以防操作失败。

我。参考案例

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

def foo(i: Int): IO[Int] = //... some effectual computation

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

二。 MVar-case

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 ()

有没有办法至少对MVarRef 实现这种行为?

UPD

我用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

【问题讨论】:

  • for (i &lt;- mvar.take; ii &lt;- foo(i); _ &lt;- mvar.put(ii)) yield ii 有什么问题,没有onError
  • @Martijn 它将使 mvar 为空导致死锁。
  • “丑陋”是主观的。 “漂亮”没有什么实用价值。你可以做例如mvar.take &gt;&gt;= (i =&gt; foo(i).attempt.map(_.fold(_ =&gt; i, ii =&gt; ii)).flatTap(mvar.put)) 但它不会自动使代码变得更好。
  • @MateuszKubuszok 好吧,IMO 丑陋会抑制可读性,所以美化它可能有一些价值......

标签: scala concurrency functional-programming scala-cats


【解决方案1】:

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

然后:

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

【讨论】:

  • 看起来好多了,谢谢。我认为将 recovery 实现为mvar.put(i) *&gt; IO(i) 以避免空 MVar 是必要的。
  • @SomeName 是唯一更新updateAtomically 内的MVar 的地方吗?如果是这样,是的。
猜你喜欢
  • 1970-01-01
  • 2020-12-22
  • 1970-01-01
  • 2011-07-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-11-13
相关资源
最近更新 更多