refined is a Scala library for refining types with type-level predicates which constrain the set of values described by the refined type. It started as a port of the refined Haskell library (which also provides an excellent motivation why this kind of library is useful).
A quick example:
import eu.timepit.refined._
import eu.timepit.refined.auto._
import eu.timepit.refined.numeric._
import shapeless.tag.@@
// This refines Int with the Positive predicate and checks via an
// implicit macro that the assigned value satisfies it:
val i1: Int @@ Positive = 5
i1: Int @@ Positive = 5
// If the value does not satisfy the predicate, we get a meaningful
// compile error:
val i2: Int @@ Positive = -5
<console>:21: error: Predicate failed: (-5 > 0).
val i2: Int @@ Positive = -5
^
// There is also the explicit refineMT macro that can infer the base
// type from its parameter:
scala> refineMT[Positive](5)
res0: Int @@ Positive = 5
// Macros can only validate literals because their values are known at
// compile-time. To validate arbitrary (runtime) values we can use the
// refineT function:
scala> refineT[Positive](5)
res1: Either[String, Int @@ Positive] = Right(5)
scala> refineT[Positive](-5)
res2: Either[String, Int @@ Positive] = Left(Predicate failed: (-5 > 0).)@@ is shapeless' type for tagging types which has the nice
property of being a subtype of its first type parameter (i.e. (T @@ P) <: T).
refined also contains inference rules for converting between different
refined types. For example, Int @@ Greater[_10] can be safely converted
to Int @@ Positive because all integers greater than ten are also positive.
The type conversion of refined types is a compile-time operation that is
provided by the library:
import shapeless.nat._
scala> val a: Int @@ Greater[_5] = 10
a: Int @@ Greater[_5] = 10
// Since every value greater than 5 is also greater than 4, a can be ascribed
// the type Int @@ Greater[_4]:
scala> val b: Int @@ Greater[_4] = a
b: Int @@ Greater[_4] = 10
// An unsound ascription leads to a compile error:
scala> val c: Int @@ Greater[_6] = a
<console>:34: error: type mismatch (invalid inference):
Greater[_5] does not imply
Greater[_6]
val b: Int @@ Greater[_6] = a
^This mechanism allows to pass values of more specific types (e.g. Int @@ Greater[_10])
to functions that take a more general type (e.g. Int @@ Positive) without manual
intervention.
- More examples
- Using refined
- Documentation
- Provided predicates
- Contributors and participation
- Projects using refined
- Performance concerns
- Related projects
- License
import shapeless.{ ::, HNil }
import eu.timepit.refined.boolean._
import eu.timepit.refined.char._
import eu.timepit.refined.collection._
import eu.timepit.refined.generic._
import eu.timepit.refined.string._
scala> refineMT[NonEmpty]("Hello")
res2: String @@ NonEmpty = Hello
scala> refineMT[NonEmpty]("")
<console>:27: error: Predicate isEmpty() did not fail.
refineMT[NonEmpty]("")
^
scala> type ZeroToOne = Not[Less[_0]] And Not[Greater[_1]]
defined type alias ZeroToOne
scala> refineMT[ZeroToOne](1.8)
<console>:27: error: Right predicate of (!(1.8 < 0) && !(1.8 > 1)) failed: Predicate (1.8 > 1) did not fail.
refineMT[ZeroToOne](1.8)
^
scala> refineMT[AnyOf[Digit :: Letter :: Whitespace :: HNil]]('F')
res3: Char @@ AnyOf[Digit :: Letter :: Whitespace :: HNil] = F
scala> refineMT[MatchesRegex[W.`"[0-9]+"`.T]]("123.")
<console>:34: error: Predicate failed: "123.".matches("[0-9]+").
refineMT[MatchesRegex[W.`"[0-9]+"`.T]]("123.")
^
scala> val d1: Char @@ Equal[W.`'3'`.T] = '3'
d1: Char @@ Equal[Char('3')] = 3
scala> val d2: Char @@ Digit = d1
d2: Char @@ Digit = 3
scala> val d3: Char @@ Letter = d1
<console>:34: error: type mismatch (invalid inference):
Equal[Char('3')] does not imply
Letter
val d3: Char @@ Letter = d1
^
scala> val r1: String @@ Regex = "(a|b)"
r1: String @@ Regex = (a|b)
scala> val r2: String @@ Regex = "(a|b"
<console>:40: error: Regex predicate failed: Unclosed group near index 4
(a|b
^
val r2: String @@ Regex = "(a|b"
^
scala> val u1: String @@ Url = "htp://example.com"
<console>:40: error: Url predicate failed: unknown protocol: htp
val u1: String @@ Url = "htp://example.com"
^Note that W
is a shortcut for shapeless.Witness which provides
syntax for literal-based singleton types.
The latest version of the library is 0.3.7, which is available for Scala and Scala.js version 2.10 and 2.11.
If you're using sbt, add the following to your build:
libraryDependencies ++= Seq(
"eu.timepit" %% "refined" % "0.3.7",
"eu.timepit" %% "refined-scalaz" % "0.3.7", // optional, JVM only
"eu.timepit" %% "refined-scodec" % "0.3.7", // optional
"eu.timepit" %% "refined-scalacheck" % "0.3.7" % "test" // optional
)
For Scala.js just replace %% with %%% above.
Instructions for Maven and other build tools are available at search.maven.org.
Release notes for the latest version are available in 0.3.7.markdown.
The optional dependencies are add-on libraries that provide support for other tag types or integration of refined types in other libraries:
refined-scalazfor support of Scalaz' tag type (scalaz.@@)refined-scodecfor integration with scodecrefined-scalacheckfor ScalaCheck type class instances of refined types
See also the list of projects that use refined for libraries that directly provide support for refined.
API documentation of the latest release is available at: http://fthomas.github.io/refined/latest/api/
There are further (type-checked) examples in the docs
directory including ones for defining custom predicates
and working with type aliases. It also contains a
description of refined's design and internals.
The library comes with these predefined predicates:
True: constant predicate that is alwaystrueFalse: constant predicate that is alwaysfalseNot[P]: negation of the predicatePAnd[A, B]: conjunction of the predicatesAandBOr[A, B]: disjunction of the predicatesAandBXor[A, B]: exclusive disjunction of the predicatesAandBAllOf[PS]: conjunction of all predicates inPSAnyOf[PS]: disjunction of all predicates inPSOneOf[PS]: exclusive disjunction of all predicates inPS
Digit: checks if aCharis a digitLetter: checks if aCharis a letterLetterOrDigit: checks if aCharis a letter or digitLowerCase: checks if aCharis a lower case characterUpperCase: checks if aCharis an upper case characterWhitespace: checks if aCharis white space
Contains[U]: checks if aTraversableOncecontains a value equal toUCount[PA, PC]: counts the number of elements in aTraversableOncewhich satisfy the predicatePAand passes the result to the predicatePCEmpty: checks if aTraversableOnceis emptyNonEmpty: checks if aTraversableOnceis not emptyForall[P]: checks if the predicatePholds for all elements of aTraversableOnceExists[P]: checks if the predicatePholds for some elements of aTraversableOnceHead[P]: checks if the predicatePholds for the first element of aTraversableIndex[N, P]: checks if the predicatePholds for the element at indexNof a sequenceLast[P]: checks if the predicatePholds for the last element of aTraversableSize[P]: checks if the size of aTraversableOncesatisfies the predicatePMinSize[N]: checks if the size of aTraversableOnceis greater than or equal toNMaxSize[N]: checks if the size of aTraversableOnceis less than or equal toN
Equal[U]: checks if a value is equal toUEval[S]: checks if a value applied to the predicateSyieldstrueConstructorNames[P]: checks if the constructor names of a sum type satisfyPFieldNames[P]: checks if the field names of a product type satisfyPSubtype[U]: witnesses that the type of a value is a subtype ofUSupertype[U]: witnesses that the type of a value is a supertype ofU
Less[N]: checks if a numeric value is less thanNLessEqual[N]: checks if a numeric value is less than or equal toNGreater[N]: checks if a numeric value is greater thanNGreaterEqual[N]: checks if a numeric value is greater than or equal toNPositive: checks if a numeric value is greater than zeroNonPositive: checks if a numeric value is zero or negativeNegative: checks if a numeric value is less than zeroNonNegative: checks if a numeric value is zero or positiveInterval.Open[L, H]: checks if a numeric value is in the interval (L,H)Interval.OpenClosed[L, H]: checks if a numeric value is in the interval (L,H]Interval.ClosedOpen[L, H]: checks if a numeric value is in the interval [L,H)Interval.Closed[L, H]: checks if a numeric value is in the interval [L,H]
EndsWith[S]: checks if aStringends with the suffixSMatchesRegex[S]: checks if aStringmatches the regular expressionSRegex: checks if aStringis a valid regular expressionStartsWith[S]: checks if aStringstarts with the prefixSUri: checks if aStringis a valid URIUrl: checks if aStringis a valid URLUuid: checks if aStringis a valid UUIDXml: checks if aStringis valid XMLXPath: checks if aStringis a valid XPath expression
- Alexandre Archambault (@alxarchambault)
- Frank S. Thomas (@fst9000)
- Jean-Rémi Desjardins (@jrdesjardins)
- Vladimir Koshelev (@vlad_koshelev)
- Your name here :-)
refined is a Typelevel project. This means we embrace pure, typeful, functional programming, and provide a safe and friendly environment for teaching, learning, and contributing as described in the Typelevel code of conduct.
If you have a project that uses the library to enforce more static guarantees and you'd like to include in this list, please open a pull request or mention it in the Gitter channel and we'll add a link to it here.
- argonaut-shapeless - provides the argonaut-refined subproject for (de)serialization of refined types from and to JSON
- circe - provides the circe-refined subproject for (de)serialization of refined types from and to JSON
- Monocle - provides the monocle-refined subproject which contains lenses for safe bit indexing into primitive types
- Quasar - is an open source NoSQL analytics engine which uses refined for natural and positive integer types
- Your project here :-)
Using refined's macros for compile-time refinement bears zero runtime overhead for reference types and only causes boxing for value types. PostErasureAnyRef and PostErasureAnyVal show the differences of unrefined and refined types during the posterasure compiler phase.
- bond: Type-level validation for Scala
- F7: Refinement Types for F#
- LiquidHaskell: Refinement Types via SMT and Predicate Abstraction
- refined: Refinement types with static and runtime checking for Haskell. refined was inspired this library and even stole its name!
refined is licensed under the MIT license, available at http://opensource.org/licenses/MIT and also in the LICENSE file.