Skip to content

Instantly share code, notes, and snippets.

@horothesun
Last active March 24, 2025 00:12
Show Gist options
  • Select an option

  • Save horothesun/958797e202dab1650a7126a510c587ae to your computer and use it in GitHub Desktop.

Select an option

Save horothesun/958797e202dab1650a7126a510c587ae to your computer and use it in GitHub Desktop.
Scala 3 Cats STM bank transfer example
//> using scala 3.6.4
//> using jvm temurin:21
//
//> using dep org.typelevel::cats-core:2.13.0
//> using dep org.typelevel::cats-effect:3.6.0
//> using dep io.github.timwspence::cats-stm:0.13.5
import cats.syntax.all.*
import cats.effect.{IO, IOApp}
import io.github.timwspence.cats.stm.*
import scala.concurrent.duration.*
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(from: TVar[Long], to: TVar[Long], amount: Long): Txn[Unit] =
for {
_ <- debit(account = from, amount)
_ <- credit(account = to, amount)
} yield ()
def credit(account: TVar[Long], amount: Long): Txn[Unit] = account.modify(_ + amount)
def debit(account: TVar[Long], amount: Long): Txn[Unit] =
for {
balance <- account.get
_ <- stm.check(balance > amount)
_ <- account.modify(_ - amount)
} yield ()
extension [A](txn: Txn[A])
def atomically: IO[A] = stm.commit(txn)
def printBalances(aliceAccount: TVar[Long], bobAccount: TVar[Long]): IO[Unit] =
(
aliceAccount.get,
bobAccount.get
).tupled
.atomically
.flatMap((a, b) => IO.println(s"Alice: $a | Bob: $b"))
for {
aliceAccount <- TVar.of(100L).atomically
bobAccount <- TVar.of(0L).atomically
_ <- printBalances(aliceAccount, bobAccount)
_ <- (
IO.sleep(1.seconds) *> credit(aliceAccount, amount = 42L).atomically,
debit(bobAccount, amount = 3L).atomically,
IO.sleep(2.seconds) *> transfer(from = aliceAccount, to = bobAccount, amount = 100L).atomically
).parTupled
_ <- printBalances(aliceAccount, bobAccount)
} yield ()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment