Version Matrix

Cats STM

Build Status Join the chat at https://gitter.im/cats-stm/community Scala Steward badge

An implementation of Software Transactional Memory for Cats Effect, inspired by Beautiful Concurrency.

For more information, see the documentation.

Usage

libraryDependencies += "io.github.timwspence" %% "cats-stm" % "0.8.0"

The core abstraction is the TVar (transactional var), which exposes operations in the STM monad. Once constructed, STM actions can be atomically evaluated in the IO monad.

Here is a contrived example of what this looks like in practice. We use the check combinator to retry transferring money from Tim and Steve until we have enough money in Tim's account:

import cats.effect.{ExitCode, IO, IOApp}
import io.github.timwspence.cats.stm.{TVar, STM}
import scala.concurrent.duration._

object Main extends IOApp {

  override def run(args: List[String]): IO[ExitCode] =
    for {
      accountForTim   <- TVar.of[Long](100).commit[IO]
      accountForSteve <- TVar.of[Long](0).commit[IO]
      _               <- printBalances(accountForTim, accountForSteve)
      _               <- giveTimMoreMoney(accountForTim).start
      _               <- transfer(accountForTim, accountForSteve)
      _               <- printBalances(accountForTim, accountForSteve)
    } yield ExitCode.Success

  private def transfer(accountForTim: TVar[Long], accountForSteve: TVar[Long]): IO[Unit] =
    STM.atomically[IO] {
      for {
        balance <- accountForTim.get
        _       <- STM.check(balance > 100)
        _       <- accountForTim.modify(_ - 100)
        _       <- accountForSteve.modify(_ + 100)
      } yield ()
    }

  private def giveTimMoreMoney(accountForTim: TVar[Long]): IO[Unit] =
    for {
      _ <- IO.sleep(5000.millis)
      _ <- STM.atomically[IO](accountForTim.modify(_ + 1))
    } yield ()

  private def printBalances(accountForTim: TVar[Long], accountForSteve: TVar[Long]): IO[Unit] =
    for {
      (amountForTim, amountForSteve) <- STM.atomically[IO](for {
        t <- accountForTim.get
        s <- accountForSteve.get
      } yield (t, s))
      _ <- IO(println(s"Tim: $amountForTim"))
      _ <- IO(println(s"Steve: $amountForSteve"))
    } yield ()

}

Documentation

The documentation is built using sbt microsites. You can generate it via sbt docs/makeMicrosite. You can view it locally via cd docs/target/site && jekyll serve.

Credits

This software was inspired by Beautiful Concurrency and informed by ZIO which has a common origin in that paper via the stm package.