Skip to content


Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?

Latest commit


Git stats


Failed to load latest commit information.

Cats STM

Build Status Latest version Discord Scala Steward badge

Composable in-memory transactions for Cats Effect which will handle locking, optimistic concurrency and automatic retries for you. The STM runtime takes care of acquiring locks in the correct order so your transactions are safe and should not deadlock. This locking is optimistic so it will only acquire the minimal set of locks and only when necessary to commit the result of a transaction.

For more information, see the microsite.


import scala.concurrent.duration._

import cats.effect.{IO, IOApp}

import io.github.timwspence.cats.stm.__

object Main extends IOApp.Simple {

  override def run: IO[Unit] = STM.runtime[IO].flatMap(run(_))

  def run(stm: STM[IO]): IO[Unit] = {
    import stm._

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

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

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

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




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


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.