I would like to define attributes in a domain entity (as in Domain Driven Design) to be of type String with a max length. Different attributes will have different max length (so that it can match the database column data type). e.g. Description will be VARCHAR2(50) while long description will be VARCHAR2(200).
Is it possible to define a type that takes a integer as is parameter like VARCHAR2(50)? So that I just need to define one class for all such types and use it for different attributes? val description: TextValue(50) val longDescription: TextValue(200)
I don't think you can do something like this using Java type system (except with code post-processing, see the last idea). Scala type system is significantly more powerful so there are a few avenues you may try to follow.
Shapeless
NatOne obvious direction is to try to use
Natprovided by shapeless which is roughly speaking a type encoding of natural numbers. You may use it like this to defineTextValueof a given max length:then you can use it like this:
The problem with this approach is that
Natsignificantly extends compilation time. If you try to compileNatfor hundreds it will take minutes and I'm not sure if you can compile thousands this way. You may also find some details at Limits of Nat type in ShapelessHandcrafted
NatCompilation time of
Natis quite bad because numbers are encoded using a kind of Church encoding with many-manySucc[_]wrappers. In practice you most probably don't need all values between 1 and your max length, so hand-crafted version that explicitly lists only the values you need might be better for you:with such custom
Natand quite similarTextValueyou can easily compile something like this
Note that this time max length of
200does not affect compilation time in any significant way.Runtime checks using implicits
You can use a totally different approach, if you are OK with all checks being runtime only. Then you can define
trait Validatorandclass ValidatedValuesuch as:and define
MaxLengthchecks asThen you can use it like this:
Note that this time the last line will compile and will only fail at runtime.
A benefit of this approach might be the fact that you can use checks on arbitrary classes rather than only
String. For example you can create something likeNonNegativeInt. Also with this approach you theoretically can combine several checks in one (but turningMaxLengthinto a trait and creating a type that extends several traits). In such case you will probably want yourvalidateto return something likecats.data.Validatedor at leastList[String]to accumulate several errors with different reasons.Runtime checks with macros
I have no ready code for this approach but the idea is that you define an annotation that is processed by a macro. You use it to annotate fields of your classes. And you write a macro that will re-write code of the class in such a way that it will verify max length (or other conditions depending on annotation) in the setter of the field.
This is the only solution that you probably can relatively easily implement in Java as well.