【发布时间】:2021-02-22 04:39:54
【问题描述】:
我想对 MVar 或 Ref 中的值应用有效的计算,并原子地更新它以防计算成功或放回初始值(如果是 @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 ()
有没有办法至少对MVar 或Ref 实现这种行为?
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 <- mvar.take; ii <- foo(i); _ <- mvar.put(ii)) yield ii有什么问题,没有onError? -
@Martijn 它将使 mvar 为空导致死锁。
-
“丑陋”是主观的。 “漂亮”没有什么实用价值。你可以做例如
mvar.take >>= (i => foo(i).attempt.map(_.fold(_ => i, ii => ii)).flatTap(mvar.put))但它不会自动使代码变得更好。 -
@MateuszKubuszok 好吧,IMO 丑陋会抑制可读性,所以美化它可能有一些价值......
标签: scala concurrency functional-programming scala-cats