Type safe Scala — Use domain types
The less efficient alternative to new types.
By domain types I mean to be able to give a more specific/well-defined semantics to an existing data type. Let’s say we have an api to work with banking account numbers, where an account number is a numeric string between nine and nineteen characters long:
Looking at those method we can immediately spot the issue of using the primitive String
data type for modelling the account number: how should we handle, in each method, the case where the account number is invalid? And the answer is… we do not!
Instead we design our API as follows:
Notice how our previous methods now can take for granted that they are working with valid account numbers, because the only way of constructing AccountNumber
instances is by going through the code guarded by a validation check, this means that whenever our code compiles we can be sure that it is as safe as strong our validation check is. The use of the Either
type is precisely to force the users of the API to deal with (or deliberately ignore) the possibility of using an invalid account number.
Improving runtime performance
One of the downsides of the previous solution is that we are creating a wrapper type which is used only at compile time to enforce invariants, but that at runtime is completely useless. Scala provides something similar to Haskell’s newtype
called value classes:
Value classes are a new mechanism in Scala to avoid allocating runtime objects. This is accomplished through the definition of new AnyVal subclasses…It has a single, public val parameter that is the underlying runtime representation. The type at compile time is wrapped, but at runtime, the representation is the underlying type.
This is great although in reality the evaporation of the wrapper type is not guaranteed to disappear and some people in the Scala community are looking for other ways to achieve it.
So, if instead of using an abstract class we could use a value class:
The private[api]
thingy is necessary to forbid direct access to the apply
method of the companion object that is automatically generated by Scala for case classes.