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:
importeu.timepit.refined._importeu.timepit.refined.api.Refinedimporteu.timepit.refined.auto._importeu.timepit.refined.numeric._// This refines Int with the Positive predicate and checks via an// implicit macro that the assigned value satisfies it:
scala>vali1:IntRefinedPositive=5
i1:IntRefinedPositive=5// If the value does not satisfy the predicate, we get a meaningful// compile error:
scala>vali2:IntRefinedPositive=-5
<console>:22:error: Predicatefailed: (-5>0).
vali2:IntRefinedPositive=-5^// There is also the explicit refineMV macro that can infer the base// type from its parameter:
scala> refineMV[Positive](5)
res0:IntRefinedPositive=5// Macros can only validate literals because their values are known at// compile-time. To validate arbitrary (runtime) values we can use the// refineV function:
scala>valx=42// suppose the value of x is not known at compile-time
scala> refineV[Positive](x)
res1:Either[String, IntRefinedPositive] =Right(42)
scala> refineV[Positive](-x)
res2:Either[String, IntRefinedPositive] =Left(Predicatefailed: (-42>0).)refined also contains inference rules for converting between different
refined types. For example, Int Refined Greater[W.`10`.T] can be safely
converted to Int Refined 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:
scala>vala:IntRefinedGreater[W.`5`.T] =10
a:IntRefinedGreater[Int(5)] =10// Since every value greater than 5 is also greater than 4, `a` can be// ascribed the type Int Refined Greater[W.`4`.T]:
scala>valb:IntRefinedGreater[W.`4`.T] = a
b:IntRefinedGreater[Int(4)] =10// An unsound ascription leads to a compile error:
scala>valc:IntRefinedGreater[W.`6`.T] = a
<console>:23:error: typemismatch (invalid inference):Greater[Int(5)] does not imply
Greater[Int(6)]
valc:IntRefinedGreater[W.`6`.T] = a
^This mechanism allows to pass values of more specific types (e.g.
Int Refined Greater[W.`10`.T]) to functions that take a more general
type (e.g. Int Refined Positive) without manual intervention.
Note that W
is a shortcut for shapeless.Witness which provides
syntax for literal-based singleton types.
- More examples
- Using refined
- Community
- Documentation
- Provided predicates
- Contributors and participation
- Performance concerns
- Related projects
- License
importeu.timepit.refined.api.RefTypeimporteu.timepit.refined.boolean._importeu.timepit.refined.char._importeu.timepit.refined.collection._importeu.timepit.refined.generic._importeu.timepit.refined.string._importshapeless.{ ::, HNil }
scala> refineMV[NonEmpty]("Hello")
res2:StringRefinedNonEmpty=Hello
scala> refineMV[NonEmpty]("")
<console>:39:error: Predicate isEmpty() did not fail.
refineMV[NonEmpty]("")
^
scala>typeZeroToOne=Not[Less[W.`0.0`.T]] AndNot[Greater[W.`1.0`.T]]
defined typealiasZeroToOne
scala> refineMV[ZeroToOne](1.8)
<console>:40:error: Right predicate of (!(1.8<0.0) &&!(1.8>1.0)) failed: Predicate (1.8>1.0) did not fail.
refineMV[ZeroToOne](1.8)
^
scala> refineMV[AnyOf[Digit::Letter::Whitespace::HNil]]('F')
res3:CharRefinedAnyOf[Digit::Letter::Whitespace::HNil] =F
scala> refineMV[MatchesRegex[W.`"[0-9]+"`.T]]("123.")
<console>:39:error: Predicatefailed: "123.".matches("[0-9]+").
refineMV[MatchesRegex[W.`"[0-9]+"`.T]]("123.")
^
scala>vald1:CharRefinedEqual[W.`'3'`.T] ='3'
d1:CharRefinedEqual[Char('3')] =3
scala>vald2:CharRefinedDigit= d1
d2:CharRefinedDigit=3
scala>vald3:CharRefinedLetter= d1
<console>:39:error: typemismatch (invalid inference):Equal[Char('3')] does not imply
Lettervald3:CharRefinedLetter= d1
^
scala>valr1:StringRefinedRegex="(a|b)"
r1:StringRefinedRegex= (a|b)
scala>valr2:StringRefinedRegex="(a|b"
<console>:38:error: Regex predicate failed: Unclosed group near index 4
(a|b
^valr2:StringRefinedRegex="(a|b"^
scala>valu1:StringRefinedUrl="htp://example.com"
<console>:38:error: Url predicate failed: unknown protocol: htp
valu1:StringRefinedUrl="htp://example.com"^// Here we define a refined type "Int with the predicate (7 <= value < 77)".
scala>typeAge=IntRefinedInterval.ClosedOpen[W.`7`.T, W.`77`.T]
scala>valuserInput=55// We can refine values with this refined type by either using `refineV`// with an explicit return type
scala>valageEither1:Either[String, Age] = refineV(userInput)
ageEither1:Either[String,Age] =Right(55)
// or by using `RefType.applyRef` with the refined type as type parameter.
scala>valageEither2=RefType.applyRef[Age](userInput)
ageEither2:Either[String,Age] =Right(55)The latest version of the library is 0.9.14, which is available for Scala and Scala.js version 2.12 and 2.13.
If you're using sbt, add the following to your build:
libraryDependencies ++=Seq(
"eu.timepit"%%"refined"%"0.9.14",
"eu.timepit"%%"refined-cats"%"0.9.14", // optional"eu.timepit"%%"refined-eval"%"0.9.14", // optional, JVM-only"eu.timepit"%%"refined-jsonpath"%"0.9.14", // optional, JVM-only"eu.timepit"%%"refined-pureconfig"%"0.9.14", // optional, JVM-only"eu.timepit"%%"refined-scalacheck"%"0.9.14", // optional"eu.timepit"%%"refined-scalaz"%"0.9.14", // optional"eu.timepit"%%"refined-scodec"%"0.9.14", // optional"eu.timepit"%%"refined-scopt"%"0.9.14", // optional"eu.timepit"%%"refined-shapeless"%"0.9.14"// 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.9.14.markdown.
The project provides these optional extensions and library integrations:
refined-catsprovides Cats type class instances for refined typesrefined-evalprovides theEval[S]predicate that checks if a value applied to the predicateSyieldstruerefined-jsonpathprovides theJSONPathpredicate that checks if aStringis a valid JSONPathrefined-pureconfigallows to read configuration with refined types using PureConfigrefined-scalacheckallows to generate arbitrary values of refined types with ScalaCheck. Userefined-scalacheck_1.13instead if your other dependencies use scalacheck version 1.13refined-scalazprovides Scalaz type class instances for refined types and support forscalaz.@@refined-scodecallows binary decoding and encoding of refined types with scodec and allows refiningscodec.bits.ByteVectorrefined-scoptallows to read command line options with refined types using scoptrefined-shapeless
Below is an incomplete list of third-party extensions and library integrations for refined. If your library is missing, please open a pull request to list it here:
- api-refiner
- argonaut-refined
- atto-refined
- case-app-refined
- circe-refined
- ciris-refined
- cormorant-refined
- coulomb-refined
- decline-refined
- doobie-refined
- exercises-refined
- extruder-refined
- finch-refined
- formulation-refined
- kantan.csv-refined
- kantan.regex-refined
- kantan.xpath-refined
- monocle-refined
- neotypes-refined
- play-refined
- play-json-refined
- play-json-refined
- refined-anorm
- refined-guava
- scanamo-refined
- seals-refined
- slick-refined
- strictify-refined
- validated-config
- vulcan-refined
- xml-names-core
If your open source project is using refined, please consider opening a pull request to list it here:
- Quasar: An open source NoSQL analytics engine which uses refined for natural and positive integer types
- rvi_sota_server: The SOTA Server Reference Implementation uses refined for domain specific types. like the vehicle identification number (VIN).
Are you using refined in your organization or company? Please consider opening a pull request to list it here:
API documentation of the latest release is available at: https://static.javadoc.io/eu.timepit/refined_2.12/0.9.14/eu/timepit/refined/index.html
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.
Talks and other external resources are listed on the Resources page in the wiki.
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 predicatesAandBNand[A, B]: negated conjunction of the predicatesAandBNor[A, B]: negated 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 aTraversablecontains a value equal toUCount[PA, PC]: counts the number of elements in aTraversablewhich satisfy the predicatePAand passes the result to the predicatePCEmpty: checks if aTraversableis emptyNonEmpty: checks if aTraversableis not emptyForall[P]: checks if the predicatePholds for all elements of aTraversableExists[P]: checks if the predicatePholds for some elements of aTraversableHead[P]: checks if the predicatePholds for the first element of aTraversableIndex[N, P]: checks if the predicatePholds for the element at indexNof a sequenceInit[P]: checks if the predicatePholds for all but the last element of aTraversableLast[P]: checks if the predicatePholds for the last element of aTraversableTail[P]: checks if the predicatePholds for all but the first element of aTraversableSize[P]: checks if the size of aTraversablesatisfies the predicatePMinSize[N]: checks if the size of aTraversableis greater than or equal toNMaxSize[N]: checks if the size of aTraversableis less than or equal toN
Equal[U]: checks if a value is equal toU
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]Modulo[N, O]: checks if an integral value moduloNisODivisible[N]: checks if an integral value is evenly divisible byNNonDivisible[N]: checks if an integral value is not evenly divisible byNEven: checks if an integral value is evenly divisible by 2Odd: checks if an integral value is not evenly divisible by 2NonNaN: checks if a floating-point number is not NaN
EndsWith[S]: checks if aStringends with the suffixSIPv4: checks if aStringis a valid IPv4IPv6: checks if aStringis a valid IPv6MatchesRegex[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 UUIDValidByte: checks if aStringis a parsableByteValidShort: checks if aStringis a parsableShortValidInt: checks if aStringis a parsableIntValidLong: checks if aStringis a parsableLongValidFloat: checks if aStringis a parsableFloatValidDouble: checks if aStringis a parsableDoubleValidBigInt: checks if aStringis a parsableBigIntValidBigDecimal: checks if aStringis a parsableBigDecimalXml: checks if aStringis well-formed XMLXPath: checks if aStringis a valid XPath expressionTrimmed: checks if aStringhas no leading or trailing whitespaceHexStringSpec: checks if aStringrepresents a hexadecimal number
The following people have helped making refined great:
- Alex
- Alexandre Archambault
- Chris Birchall
- Chris Hodapp
- Cody Allen
- Dale Wijnand
- Denys Shabalin
- Derek Morr
- Didac
- Diogo Castro
- dm-tran
- Frank S. Thomas
- Howard Perrin
- Iurii Susuk
- Jean-Rémi Desjardins
- Jente Hidskes
- Joe Greene
- John-Michael Reed
- kenji yoshida
- kusamakura
- Leif Wickland
- Luis Miguel Mejía Suárez
- Matt Pickering
- Michael Thomas
- Naoki Aoyama
- Nicolas Rinaudo
- Olli Helenius
- Richard Gomes
- ronanM
- Sam Halliday
- Shohei Shimomura
- Tim Steinbach
- Torsten Scholak
- Viktor Lövgren
- Vladimir Koshelev
- Yuki Ishikawa
- Zainab Ali
- 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 Scala Code of Conduct.
Using refined's macros for compile-time refinement has zero runtime overhead for reference types and only causes boxing for value types. Refer to RefineJavapSpec and InferJavapSpec for a detailed analysis of the runtime component of refinement types on the JVM.
- SMT-Based Checking of Predicate-Qualified Types for Scala
- bond: Type-level validation for Scala
- F7: Refinement Types for F#
- LiquidHaskell: Refinement Types via SMT and Predicate Abstraction
- Refinement Types in Typed Racket
- refined: Refinement types with static and runtime checking for Haskell. refined was inspired this library and even stole its name!
- refscript: Refinement Types for TypeScript
- Subtypes in Perl 6
refined is licensed under the MIT license, available at http://opensource.org/licenses/MIT and also in the LICENSE file.