Library / SDK
fthomas/refined avatar
fthomas/refined

refined for Scala: Type-Level Predicates and Runtime Refinement

Refinement types for Scala

1,733 stars151 forksScalaMIT

At a glance

What is it?
fthomas/refined adds compile-time and runtime validation to Scala types through type-level predicates. It fits code that needs stronger guarantees than ordinary `Int` or `String`, but its macros and implicit refinement machinery add type-system complexity.
Who is it for?
Scala teams should consider refined when invalid values are expensive enough to justify predicate-aware APIs and compile-time checks, while developers validating only arbitrary runtime input may prefer the simpler `Either` boundary without adopting its full type machinery. Check the chosen predicates, Scala version support, implicit imports, and actual validation cost before adoption.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 7 days ago.
What is it written in?
Mainly Scala, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

refined's Core Type Boundary

refined addresses a gap in ordinary Scala types. An `Int` can represent negative numbers, zero, and positive numbers, while a `String` can represent an empty string, malformed input, and meaningful text. refined narrows those broad types by attaching a predicate. The resulting type, such as `Int Refined Positive`, tells the compiler which values are allowed.

The intended audience is Scala developers who want illegal states to fail at compile time or at a controlled runtime boundary. A function accepting only `Int Refined Positive` does not accept an unrestricted `Int` by accident. The caller must provide a value that satisfies `Positive`. This is useful when a zero or negative value would break later logic, or when malformed configuration should be rejected before it reaches the rest of the system.

The README describes refined as a port of the Haskell `refined` library and notes that the Scala approach was previously explored in `bond`. That distinction matters. refined does not replace Scala's value classes or ordinary `Either` error handling. It combines a base type with a type-level predicate, then supplies mechanisms for constructing values and proving relationships between refined types.

Literal Validation Through Scala Macros

For values known while the compiler processes the code, refined provides implicit macro validation. The README imports the core package, the `Refined` type, automatic refinements, and numeric predicates:

scala
import eu.timepit.refined._
import eu.timepit.refined.api.Refined
import eu.timepit.refined.auto._
import eu.timepit.refined.numeric._

With those imports, the following declaration checks a literal against `Positive` during compilation:

scala
val i1: Int Refined Positive = 5

The documented result is `i1: Int Refined Positive = 5`. Replacing `5` with `-5` produces a compile-time error stating `Predicate failed: (-5 > 0).` This is the library's clearest value. A constraint attached to the type is checked where the value is introduced rather than waiting for an assertion or a distant validation branch.

The mechanism is not unlimited. The README explicitly says that macros can validate literals because their values are known at compile time. A value computed at runtime cannot be checked by the same macro. That boundary means refined provides two validation paths: compile-time construction for literals and an explicit function for arbitrary values.

Runtime Values Through refineV

For values that are not known at compile time, refined exposes `refineV`. It returns `Either[String, Refined]`, placing the validation error in `Left` and the accepted refined value in `Right`. The distinction makes the success and failure paths part of the return type.

The README gives this runtime example after describing `val x = 42` as a value whose value is not available to a literal-checking macro:

scala
val x = 42

refineV[Positive](x)

The documented result is `Right(42)`. For `-x`, the documented result is `Left(Predicate failed: (-42 > 0).)` Unlike the implicit literal macro, this function does not turn an invalid value into a compile error. It reports failure as data that the surrounding program must handle.

That design fits validation at input boundaries. Configuration loading, command-line parsing, or external service responses can use the base type first, then call `refineV` before passing the value into code requiring a refined type. The README does not provide a complete installation command or dependency declaration, so the correct artifact coordinates and Scala-specific dependency settings cannot be confirmed from the documented examples.

Predicate Composition Across Types

refined includes inference rules for converting between refined types when one predicate implies another. The README uses numeric predicates to show that `Int Refined Greater[10]` can be assigned to `Int Refined Positive`. The reasoning is type-level: every integer greater than ten is also greater than zero.

The documented conversion is performed through ordinary ascription:

scala
val a: Int Refined Greater[5] = 10
val b: Int Refined Greater[4] = a

The first value has the narrower constraint. The second declaration is accepted because `Greater[5]` implies `Greater[4]`. Attempting the reverse kind of invalid assignment, from `Greater[5]` to `Greater[6]`, fails at compile time with an invalid inference error. This lets a function receive a more general refined type when a more specific refined value is available, without a manual conversion function.

The library also supports predicates beyond numeric ranges. README examples cover `NonEmpty` for strings, boolean predicates, character predicates, collection predicates, generic predicates, and `MatchesRegex`. A type alias can combine predicates, as shown by `ZeroToOne`, which uses `Not[Less[0.0]]` and `Not[Greater[1.0]]`. These examples indicate a broad predicate catalog, but the README excerpt does not enumerate every predicate or explain every failure message.

Strings, Characters, and Validation Scope

The library applies the same refinement model to several base types. `String Refined NonEmpty` rejects the empty string. `Char Refined Digit`, `String Refined Regex`, and `String Refined Url` demonstrate predicates tied to characters and strings. In the regex example, a value that does not match `[0-9]+` causes a macro error. In the URL example, `htp://example.com` produces a documented protocol error.

Predicate composition is useful, but it also makes the type definition part of the API design. A type such as `Age = Int Refined Interval.ClosedOpen[7, 77]` encodes a domain rule directly in the signature. The README then introduces `refineV` for refining user input with that type. This can prevent an age below 7 or at least 77 from entering functions that accept `Age`.

There is an important limitation in interpretation. A `Regex` predicate checks that a string parses as a regular expression, not that the expression is safe for a particular application. A `Url` predicate checks the library's URL condition, but the README does not document network access, security policy, or downstream URL handling. refined improves type boundaries. It does not replace domain-specific authorization, syntax validation, or input sanitation.

Installation Evidence and Maintenance Boundaries

The README does not give a complete `sbt` dependency block or a step-by-step installation command. It points to the Scala version support index, Scaladoc, and the repository's build files, but it does not state in the excerpt which dependency notation to copy for a new project. For a first use, the documented REPL-style imports and `refineMV` example are usable as an API tutorial once the library is available in the developer's Scala environment.

The repository is not archived, and its last push was on 2026-10-16. The release list includes `v0.11.4` on 2026-06-30, after `v0.11.3` on 2024-12-27 and `v0.11.2` on 2024-06-01. This indicates recent project activity, although the version gap shows that releases are not necessarily frequent. The README contains Scala 2.13 and earlier Scala 2.12-specific guidance, including `shapeless.W` for literal singleton types before Scala 2.13.

The repository is licensed under MIT. That permits use, modification, and redistribution under the license terms, but adopters must preserve the required notices and review the license text themselves. The documented compatibility badges and Scala-specific examples make compiler version verification a required first step.

Editorial conclusion

Scala teams should consider refined when invalid values are expensive enough to justify predicate-aware APIs and compile-time checks, while developers validating only arbitrary runtime input may prefer the simpler `Either` boundary without adopting its full type machinery. Check the chosen predicates, Scala version support, implicit imports, and actual validation cost before adoption. For refined, the first project-specific decision is whether the macro and implicit machinery belongs at the core boundary of the application.

Frequently asked questions

What is refined used for in Scala?

refined expresses constraints as type-level predicates, such as `Int Refined Positive` or `String Refined NonEmpty`. It can validate literals during compilation and check runtime values through `refineV`.

How does refined validate a runtime value?

Call `refineV[Predicate](value)` with the desired predicate and runtime value. The documented return type is `Either[String, Refined]`, with a successful refined value in `Right` and a predicate error in `Left`.

Can refined convert one refined type into another?

Yes, when the source predicate implies the target predicate. The README shows that `Int Refined Greater[10]` can be used where `Int Refined Positive` is expected, while an unsupported conversion such as `Greater[5]` to `Greater[6]` fails at compile time.

Official sources

  1. fthomas/refined on GitHub
  2. Issues
  3. License: MIT
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/fthomas-refined.svg)](https://hysenlabs.com/projects/fthomas-refined)

Was this useful?