Skip to content

Instantly share code, notes, and snippets.

@otobrglez
Created July 25, 2026 22:15
Show Gist options
  • Select an option

  • Save otobrglez/fa066c1086e8d26ab2a9e51e4e87b440 to your computer and use it in GitHub Desktop.

Select an option

Save otobrglez/fa066c1086e8d26ab2a9e51e4e87b440 to your computer and use it in GitHub Desktop.
Type-safe compilation with Scala 3 and macros
// Oto Brglez - <otobrglez@gmail.com> - Jul 2026
import scala.annotation.StaticAnnotation, scala.quoted.*
final case class range(val lo: Int, val hi: Int) extends StaticAnnotation
object Validate:
inline def validate[T](inline o: T): Unit = ${ validateI[T]('o) }
private def validateI[T: Type](o: Expr[T])(using Quotes): Expr[Unit] =
import quotes.reflect.*
val rangeT = TypeRepr.of[range]
val params = TypeRepr.of[T].typeSymbol.primaryConstructor.paramSymss.flatten.filter(!_.isType)
o.asTerm.underlyingArgument match
case Apply(Select(_, "apply"), args) =>
params.zip(args).foreach: (p, arg) =>
val bounds = p.annotations.collectFirst:
case Apply(Select(New(tt), _), List(Literal(IntConstant(lo)), Literal(IntConstant(hi))))
if tt.tpe =:= rangeT => lo -> hi
bounds -> arg match
case Some(lo -> hi) -> Literal(IntConstant(v)) if v < lo || v > hi =>
report.errorAndAbort(s"${p.name} = $v is outside [$lo, $hi]", arg.pos)
case _ => ()
case _ => report.errorAndAbort("tsValidate requires construction.")
'{ () }
// Demo (separate file)
final case class Config(
@range(1, 65_535) port: Int,
@range(1, 256) maxThreads: Int,
@range(100, 30_000) timeoutMs: Int
)
@main def demoApp: Unit =
import Validate.*
validate(Config(port = 1, maxThreads = 10, timeoutMs = 3233))
validate(Config(port = -12, maxThreads = 10, timeoutMs = 3233))
// Compile-time explosion:
// scala-cli run Validate.scala TSValidationApp.scala --server=false
// Warning: setting /Users/oto/Projects/scala-sandbox as the project root directory for this run.
// -- Error: /Users/oto/Projects/scala-sandbox/TSValidationApp.scala:13:25 --------
// 13 | validate(Config(port = -12, maxThreads = 10, timeoutMs = 3233))
// | ^^^
// | port = -12 is outside [1, 65535]
// 1 error found
// Compilation failed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment