【发布时间】:2020-10-28 07:35:04
【问题描述】:
考虑以下简单的 F 代数
trait Test[F[_]] {
def add(i: Int): F[Unit]
}
我想提供它的实现来跟踪所有添加的值并添加那些尚未添加的值。它必须以线程安全的方式完成。
我能想到的“最佳”实现是使用MVar[F, Ref[F, List[Int]]]。这是它的样子
def statefulMutexTest[F[_]: Concurrent] = {
val mvarRef: F[MVar[F, Ref[F, List[Int]]]] =
for {
ref <- Ref.of[F, List[Int]](List.empty[Int])
mvar <- MVar.of[F, Ref[F, List[Int]]](ref)
} yield mvar
mvarRef map { mvar =>
new Test[F] {
override def add(i: Int): F[Unit] =
mvar.take.bracket(ref =>
for {
list <- ref.get
_ <- if (!list.contains(i)) ref.set(list :+ i) else Applicative[F].unit
} yield ())(mvar.put)
}
}
}
它看起来很乱,但按预期工作。我最初想使用StateT,但我不喜欢将状态导出给客户端的想法。
【问题讨论】:
-
AFAIK 这不是所谓的“F-代数”。这是“代数F”。 “F-代数”是
F[A] => A,是关于组合/提取的,你有Int => F[Unit]。 -
为什么还要同时使用
Ref和MVar?Ref在你的情况下就足够了。 -
@MateuszKubuszok
MVar的原因是我想提供对状态的“互斥”访问(List[Int])。如果add逻辑中涉及一些有效的计算,则似乎不可能将MVar扔掉。 -
Refalready 提供互斥 -
在这种情况下,我既不会看 Ref 也不会看 MVar,而是看 STM 实现,比如github.com/TimWSpence/cats-stm
标签: scala functional-programming scala-cats cats-effect