Tagless Final via Cats

Usage

trait Algebra[F[_]] {
  def readEnv(name: String): F[String]
  def readLn: F[String]
  def write(output: String): F[Unit]
}
class Program[F[_]: cats.Monad](x: Algebra[F]) {

  // `flatMap` extension method
  import cats.implicits.toFlatMapOps

  // `map` extension method
  import cats.implicits.toFunctorOps

  def enProgram(): F[Unit] =
    for {
      _    <- x.write("What's your name? ")
      name <- x.readLn
      _    <- x.write(s"Hello, ${name}!\n")
    } yield ()

  def esProgram(): F[Unit] =
    for {
      _    <- x.write("¿Cómo te llamas? ")
      name <- x.readLn
      _    <- x.write(s"¡Hola, ${name}!\n")
    } yield ()

  def program: F[Unit] =
    for {
      lang <- x.readEnv("LANG")
      _    <- if (lang.startsWith("es")) {
                esProgram
              } else {
                enProgram
              }
    } yield ()
}
object Interpreter extends Algebra[cats.Id] {
  override def readEnv(name: String): cats.Id[String] = sys.env(name)
  override def readLn: cats.Id[String] = scala.io.StdIn.readLine()
  override def write(output: String): cats.Id[Unit] = print(output)
}

new Program(Interpreter).program

Demo

This file is literate Scala, and can be run using Codedown:

$ LANG=es_MX.UTF-8 scala-cli --scala 2.13.10 --dependency org.typelevel::cats-core:2.3.0 \
  <(curl https://earldouglas.com/posts/effect-systems/cats.md | codedown scala)
¿Cómo te llamas? James
¡Hola, James!