Skip to content

Instantly share code, notes, and snippets.

@joshuakfarrar
Last active March 20, 2023 20:37
Show Gist options
  • Save joshuakfarrar/2a4411cc1db72561c1aba30e40ad6545 to your computer and use it in GitHub Desktop.
Save joshuakfarrar/2a4411cc1db72561c1aba30e40ad6545 to your computer and use it in GitHub Desktop.
A calculation DSL using the Free monad.
ThisBuild / version := "0.1.0-SNAPSHOT"
ThisBuild / scalaVersion := "3.2.2"
lazy val root = (project in file("."))
.settings(
name := "gpt3.5-free-monad",
libraryDependencies ++= Seq(
"org.typelevel" %% "cats-core" % "2.9.0",
"org.typelevel" %% "cats-free" % "2.9.0",
"org.typelevel" %% "cats-effect" % "3.4.8"
)
)
import cats._
import cats.effect.{IO, IOApp}
import cats.free.Free
sealed trait Calculation[A]
case class Add(value: Int) extends Calculation[Unit]
case class Subtract(value: Int) extends Calculation[Unit]
case class Multiply(value: Int) extends Calculation[Unit]
case class Divide(value: Int) extends Calculation[Unit]
case object Clear extends Calculation[Unit]
case object Result extends Calculation[Int]
type CalculationProgram[A] = Free[Calculation, A]
def add(value: Int): CalculationProgram[Unit] = Free.liftF(Add(value))
def subtract(value: Int): CalculationProgram[Unit] = Free.liftF(Subtract(value))
def multiply(value: Int): CalculationProgram[Unit] = Free.liftF(Multiply(value))
def divide(value: Int): CalculationProgram[Unit] = Free.liftF(Divide(value))
val clear: CalculationProgram[Unit] = Free.liftF(Clear)
val result: CalculationProgram[Int] = Free.liftF(Result)
def calculate: CalculationProgram[Int] = {
for {
_ <- add(5)
_ <- multiply(2)
_ <- subtract(1)
_ <- divide(4)
_ <- clear
_ <- add(10)
res <- result
} yield res
}
object Main extends IOApp.Simple {
def run: IO[Unit] = {
val program = calculate
val resultIO: IO[Int] = IO {
program.foldMap(new (Calculation ~> Id) {
def apply[A](fa: Calculation[A]): A = fa match {
case Add(value) => println(s"Adding $value"); ().asInstanceOf[A]
case Subtract(value) => println(s"Subtracting $value"); ().asInstanceOf[A]
case Multiply(value) => println(s"Multiplying by $value"); ().asInstanceOf[A]
case Divide(value) => println(s"Dividing by $value"); ().asInstanceOf[A]
case Clear => println("Clearing"); ().asInstanceOf[A]
case Result => 15.asInstanceOf[A] // Set the initial state of the calculator to 15
}
})
}
resultIO.flatMap(result => IO(println(s"Result: $result")))
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment