Skip to content

Instantly share code, notes, and snippets.

@joshuakfarrar
Created March 20, 2023 20:38
Show Gist options
  • Save joshuakfarrar/f1b7cc4693dd20d1d6e38e76292ac834 to your computer and use it in GitHub Desktop.
Save joshuakfarrar/f1b7cc4693dd20d1d6e38e76292ac834 to your computer and use it in GitHub Desktop.
An example of CommutativeMonad and tailRecM for "fold but with early termination"
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.6.1",
"org.typelevel" %% "cats-effect" % "3.2.9"
)
)
import cats._
import cats.effect.{IO, IOApp, ExitCode}
import cats.syntax.all._
object Main extends IOApp.Simple {
case class CustomState[A](value: A, remaining: Int)
implicit val customStateMonad: CommutativeMonad[CustomState] = new CommutativeMonad[CustomState] {
def pure[A](a: A): CustomState[A] = CustomState(a, 0)
def flatMap[A, B](sa: CustomState[A])(f: A => CustomState[B]): CustomState[B] = {
val sb = f(sa.value)
CustomState(sb.value, sa.remaining + sb.remaining)
}
def tailRecM[A, B](a: A)(f: A => CustomState[Either[A, B]]): CustomState[B] = {
val init = f(a)
init.value match {
case Left(nextA) =>
tailRecM(nextA)(f)
case Right(result) =>
CustomState(result, init.remaining)
}
}
}
def sum(n: Int): CustomState[Int] =
customStateMonad.tailRecM((0, n)) { case (acc, remaining) =>
if (remaining <= 0) customStateMonad.pure(Right(acc))
else customStateMonad.pure(Left((acc + remaining, remaining - 1)))
}
override def run: IO[Unit] = {
val result = sum(10).value
IO(println(s"Result: $result"))
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment