Reduce the domain of your types with Refined
Some days ago one of my coworker asked me about Refined. He said he couldn’t find any explanation about what
Refined does. After some thinking I came back with this :
Refined let you reduce the domain of your Types.
But first, what is
Refined is a Scala library. More precisely it’s a port of an
Haskell library to
Scala. Even if it came from
Haskell, it’s not complicated to use. It is complicated under the hood but that’s out of this article’s scope. Let’s just say
Refined let you write this:
It’s pretty self-explaining but let’s review some of it. The first line defines a new type by combining three existing ones. The base type is an
Int and the predicate type is
Positive. It’s this predicate that defines the constraint we add to the
As per this example, we see
Refined is about constraining existing types to define new ones. In other words
Refined reduces the value a type can take, making it more specific.
A typical use case would be to ensure the boundaries of an id. Let’s say we integrate into an existing system and we need an id beginning after 400.000. We can validate the input data but we can’t enforce the information in the system. We have no way to make sure the 400.000 rule hasn’t been broken somewhere else.
To avoid this kind of error we could define:
Using this type where we would have used
Int for our
Id has two advantages : we ensure the 400.000 rule and we clearly state that this value is an id.
By defining the
Id type, based on the
Int type we have selected a subset of
Before our refinement, the type possible values were
Int.MaxValue. After the refinement our new type possible values are 400.000 to
Int.MaxValue, which is literally a subset of the first interval.
In this sense,
Refined is a way to define types which are subsets of others. And it allows to do this by simple Type Level Programming.
Domain Reduction in Scala (And other Object Oriented Languages)
Domain reduction is used in Object Oriented Programming more often than you think.
Our Id type could have been defined this way:
By limiting the valid value in
apply we reduce the domain of our attribute, making the
Id type a subset of
Int. More generally, when one type inherits from another it defines a specific case of a more general one.
In the example, the
Dog type is one of the two value the
Animal type can take. In this sense, inheritance is about Domain Reduction.
We have seen that
Refined allows us to create new types by constraining other types. This is what I call Domain Reduction but it’s not limited to
Refined. Creating a class to limit the value of an unique attribute is about the same, as is inheritance.
Refined simply let us enforce our business rules into our types without all the boilerplate of defining classes and unsafe validations for each.