Skip to content

Instantly share code, notes, and snippets.

@lachezar
Last active October 8, 2024 11:20
Show Gist options
  • Save lachezar/c0c373c17c0cd39d2ce603cc988f3ae6 to your computer and use it in GitHub Desktop.
Save lachezar/c0c373c17c0cd39d2ce603cc988f3ae6 to your computer and use it in GitHub Desktop.
ReaderT + EitherT stack
import cats.*
import cats.syntax.all.*
import cats.data.*
import cats.effect.*
trait AppError(val msg: String)
case object InvalidConfig extends AppError("ERROR: invalid config :(")
final case class Config(port: Int)
type Result = String
val validConfig: Function1[Config, Boolean] = (config) => config.port % 3 == 2
val serviceCall: Function1[Config, IO[String]] = (config) =>
s"Running service on port ${config.port}".pure
type EitherApp[A] = EitherT[IO, AppError, A]
// defined type alias EitherApp
type Stack[A] = ReaderT[EitherApp, Config, A]
// defined type alias Stack
def program: Stack[Result] = for {
config <- ReaderT.ask[EitherApp, Config]
_ <- ReaderT
.liftF(
EitherT.liftF[IO, AppError, Unit](
IO.raiseError(new RuntimeException("runtime error"))
)
)
.whenA(config.port % 3 == 0)
_ <-
if (validConfig(config)) ().pure[Stack]
else ReaderT.liftF[EitherApp, Config, Unit](EitherT.leftT(InvalidConfig))
result <- ReaderT.liftF(
EitherT.liftF[IO, AppError, Result](serviceCall(config))
)
_ <- ReaderT.liftF(EitherT.liftF[IO, AppError, Unit](IO.println(result)))
} yield result
// program: Stack[Result]
object MyApp extends IOApp {
override def run(args: List[String]): IO[ExitCode] =
val rand = new scala.util.Random
program
.run(Config(port = rand.nextInt(3)))
.foldF(
err => IO.println(err.msg),
result => IO.println(result)
)
.onError(ex => IO.println(s"Unexpected exception: $ex"))
.as(ExitCode.Success)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment