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.10.1"

The core abstraction is the TVar (transactional var), which exposes operations in the Txn monad. Once constructed, Txn 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 scala.concurrent.duration._

import cats.effect.unsafe.implicits.global
import cats.effect.{IO, IOApp}

object Main extends IOApp.Simple {

  val stm = STM.runtime[IO].unsafeRunSync()
  import stm._

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

  private def transfer(accountForTim: TVar[Long], accountForSteve: TVar[Long]): IO[Unit] =
    stm.commit {
      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.commit(accountForTim.modify(_ + 1))
    } yield ()

  private def printBalances(accountForTim: TVar[Long], accountForSteve: TVar[Long]): IO[Unit] =
    for {
      (amountForTim, amountForSteve) <- stm.commit(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 docusaurus. You can generate it via nix-shell --run "sbt docs/docusaurusCreateSite" . You can then view it via nix-shell --run "cd website && npm start".

Credits

This software was inspired by Beautiful Concurrency and the stm package.

Many thanks to @impurepics for the awesome logo!

Tool Sponsorship

Development of Cats STM is generously supported in part by YourKit through the use of their excellent Java profiler.