Skip to content

Instantly share code, notes, and snippets.

@kubukoz
Created March 29, 2025 13:31
Show Gist options
  • Save kubukoz/9cf1f6698f9c3e4c7280b1d5e16d3979 to your computer and use it in GitHub Desktop.
Save kubukoz/9cf1f6698f9c3e4c7280b1d5e16d3979 to your computer and use it in GitHub Desktop.
reviving an old concept for a new tagless final encoding in scala 3, thanks to inspiration from Noel Welsh's scalar 2025 talk
//> using dep org.typelevel::cats-effect:3.6.0
import cats.effect.IO
import cats.effect.unsafe.IORuntime
import cats.effect.IOApp
enum Permission {
case View
case Edit
case Both(p1: Permission, p2: Permission)
}
trait Dsl {
type F[_]
def requires(p: Permission): F[Unit]
def sequence[A, B](p1: F[A], p2: F[B]): F[B]
}
object Dsl {
type Program[A] = (dsl: Dsl) ?=> dsl.F[A]
def requires(
p: Permission
): Program[Unit] = dsl.requires(p)
def sequence[A, B](
p1: Program[A],
p2: Program[B],
): Program[B] = dsl.sequence(p1, p2)
def dsl(
using instance: Dsl
): instance.type = instance
def compile[A](f: Program[A]): (dryRun: Boolean) => IO[Option[A]] =
dryRun => {
val describe =
new Dsl:
type F[A] = String
def requires(p: Permission): String = s"requires $p"
def sequence[A, B](p1: String, p2: String): String = s"$p1, $p2"
val run =
new Dsl:
type F[A] = IO[A]
def requires(p: Permission): IO[Unit] = IO.println(s"checking $p")
def sequence[A, B](p1: IO[A], p2: IO[B]): IO[B] = p1 *> p2
if (dryRun)
IO.println(
f(
using describe
)
).as(None)
else
f(
using run
).map(Some(_))
}
}
object Main extends IOApp.Simple {
val p: Dsl.Program[Unit] = Dsl.sequence(
Dsl.requires(Permission.View),
Dsl.requires(Permission.Edit),
)
def run: IO[Unit] = Dsl
.compile(p)(dryRun = false)
.flatMap(IO.println(_))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment