1

Consider the following simple F-algebra

trait Test[F[_]] {
  def add(i: Int): F[Unit]
}

I want to provide implementation of it that tracks all of the added values and adds those that have not been added yet. It has to be done in a thread safe manner.

The "best" implementation I could come up with is using MVar[F, Ref[F, List[Int]]]. Here is how it looks like

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

It looks pretty messy, but works as expected. I initially thought of using StateT, but I don't like the idea that statefulness is exported to clients.

4

1 回答 1

3

Basically Ref is everything I would normally use for such case and Test here is just a domain-specific wrapper:

object Test {

  def of[F[_]: Sync]: F[Test[F]] =
    Ref.of[F, Set[Int]](Set.empty).map(ref => (i: Int) => ref.update(_ + i).void)
}
于 2020-07-08T08:57:23.963 回答