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.

Refined

But first, what is Refined ? 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 Int type.

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.

Domain Reduction

By defining the Id type, based on the Int type we have selected a subset of Int.

Before our refinement, the type possible values were Int.MinValue to 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.

Conclusion

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.