Skip to content

0.1.0 - Compile-Time Refined Validation on Scala 3

Latest

Choose a tag to compare

@MateuszKubuszok MateuszKubuszok released this 11 Jun 06:57
· 3 commits to master since this release

The first release of refined-compat — compile-time refinement validation for refined types on Scala 3, powered by Hearth's Expr.semiEval.

On Scala 2, refined's import eu.timepit.refined.auto._ validates literal assignments at compile time. Those macros rely on c.eval, which was never ported to Scala 3 (refined#932), so val x: Int Refined Positive = 5 simply does not compile there. This library brings that experience back — on both Scala 2.13 and Scala 3:

import hearth.refined.auto._

val x: Int Refined Positive = 5     // compiles on both Scala 2.13 and 3
val y: String Refined NonEmpty = "" // fails at compile time on both

Highlights

  • hearth.refined.auto._ — a drop-in replacement for eu.timepit.refined.auto._

    • autoRefineV — implicit TRefined[T, P] with compile-time validation
    • autoInfer — implicit Refined[T, A]Refined[T, B] when Inference[A, B] holds
    • autoUnwrap — implicit F[T, P]T
  • refineMV[P](value) — explicit compile-time validation, like refined's own refineMV but working on Scala 3 too.

  • RefinedTypeOpsM[FTP, T] — companion-object helper for custom refined types (object PosInt extends RefinedTypeOpsM[PosInt, Int], then PosInt(42) validates at compile time).

  • Wide predicate coverage — works for any predicate whose Validate instance can be reconstructed from the classpath, verified by tests for:

    • numeric: Positive, Negative, NonNegative, NonPositive, Greater, Less, Interval.Closed/Open, Modulo, on Int/Long/Double
    • string: MatchesRegex, StartsWith, EndsWith, Uuid, Trimmed, ValidInt/ValidLong/ValidDouble
    • collection: NonEmpty, Size, MinSize, MaxSize
    • generic & boolean composition: Equal, And, Or, Not, and type aliases like PosInt, PortNumber
  • Cross-platform and cross-version — published for Scala 2.13 and Scala 3 (LTS) on JVM, Scala.js, and Scala Native.

  • Futureproofed for JDKs without sun.misc.Unsafe — built with Scala 3.3.8 and -Yfuture-lazy-vals on JVM, matching Hearth 0.3.1.

How it works

Hearth's semiEval reconstructs both the literal value and the Validate[T, P] (or Inference[A, B]) instance from the expression AST at macro-expansion time, runs the validation, and either emits Refined.unsafeApply(value) or aborts compilation with the predicate's error message. No c.eval needed — which is exactly why it works on Scala 3.

refined-compat doubles as a compact, real-world example of a Hearth-based macro library — alongside Kindlings.

Summary

If you have refined types and want compile-time-validated literals on Scala 3 (or a single import that works across 2.13 and 3), replace eu.timepit.refined.auto._ with hearth.refined.auto._ and you're done.

Star the project ⭐ and leave us some feedback!