What's the correct way to enforce constraints on case class values

scala

Solution

This is pretty subjective, but I'll try.

It depends on how your class is constructed. In the case of a simple utility range class used casually in internal code, any programmer using it should probably be aware that feeding a range wrong values might lead to weird results. You can assume that sane uses of the classes really don't encounter this problem, and throwing an exception in these rare cases might not be a bad practice to signal somebody really screwed up. If it is obvious from context what values should be fed to the class or not, I don't think it is particularly in bad taste to throw exceptions if people fail to meet these expectations of sanity.

Compare this to the behaviour of `NullPointerException`s or `ArithmethicException`s which still happen within Scala sometimes. Sometimes it just isn't reasonable anymore to program defensively when faced with enough 'insanity'.

On the other hand, if your class is filled by values you have less control over, i.e. they are the direct consequence of user input, you have to simply take greater control over your construction. Create a companion object with a function that returns an `Option` or an `Either`:

 object Range {
     def createSafe(from: Int, to: Int): Option[Range] = {
         if(from >= 0 && to >= 0 && to >= from)
            Some(Range(from, to))
         else
            None
     }
 }

You can then even reuse the validation logic in the instantiation of your case class by overriding `apply` as the other answer suggested.

 object Range {
    def createSafe ...
    def apply(from: Int, to: Int): Range = {
        createSafe(from, to).getOrElse(throw new IllegalArgumentException(...))
    }
 }

Problem

Throwing exceptions at construction time is generally seen as not a very polite thing to do, but I'm not sure how to avoid that when trying to enforce constraints on case class values. Say for example that I need to represent a range, and that both boundaries must be positive. An immediate implementation would be: ``` case class Range(from: Int, to: Int) ``` This, however, does not make sure that both `from` and `to` are positive, nor that `to` is greater than `from`. My first instinct is to implement it as follows: ``` case class Range(from: Int, to: Int) { require(from >= 0) require(to >= 0) require(to >= from) } ``` This, however, makes `Range`'s constructor unsafe. Is there a common pattern to keep the ease of use of case classes, enforce value constraints, and avoid throwing exceptions?

Original source