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 bothHighlights
-
hearth.refined.auto._— a drop-in replacement foreu.timepit.refined.auto._autoRefineV— implicitT→Refined[T, P]with compile-time validationautoInfer— implicitRefined[T, A]→Refined[T, B]whenInference[A, B]holdsautoUnwrap— implicitF[T, P]→T
-
refineMV[P](value)— explicit compile-time validation, like refined's ownrefineMVbut working on Scala 3 too. -
RefinedTypeOpsM[FTP, T]— companion-object helper for custom refined types (object PosInt extends RefinedTypeOpsM[PosInt, Int], thenPosInt(42)validates at compile time). -
Wide predicate coverage — works for any predicate whose
Validateinstance can be reconstructed from the classpath, verified by tests for:- numeric:
Positive,Negative,NonNegative,NonPositive,Greater,Less,Interval.Closed/Open,Modulo, onInt/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 likePosInt,PortNumber
- numeric:
-
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-valson 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!