How can I enforce compilation time limits on values ​​for Scala methods?

I want to use constraints on parameter values ​​for Scala methods at compile time.

For instance:

case class Foo(numberOfFoo: Int, ...) 

numberOfFoo above Int , but I really would like to make it a positive integer. I tried classes like PositiveInt to force this, but it just pushes the check to another class where compile time is still not checked.

Using the above example, I want:

 val n: Int = ... val f: Foo = Foo(n) 

to compile if n > 0 and not compile if n <= 0 . I do not want the instance code to handle a possible exception, handle Option[Foo] or end with Foo , where Foo.numberOfFoo != n (i.e. I do not want to use the absolute value of the input parameter).

UPDATE: Thanks for the helpful information. This is how I was afraid. Basically, I would like to indicate the size of something that should have a positive integral size. So this is the best approach:

 case class Foo(bar: Bar) {val n = bar size} 
+6
source share
2 answers

Another way to do this is to use the shapeless library and use Nat . The limitation is that you will need to create instances of Foo at compile time, mostly with known constants.

 import shapeless.ops.nat_ import shapeless.nat._ case class Foo[T <: Nat](numberOfFoo: Int)(implicit ev: GT[T, _0] object Foo { // The problem is this won't work. def apply[T <: Nat](n: Int): Foo[T] = Foo(Nat(n)) } 

This will only work when used as follows:

 Foo(_1) 

Where _1 comes from shapeless.nat._ . If you move on to implementation, part 0 is forced, even if you don't matter formless:

  if (n < 0) c.abort(c.enclosingPosition, s"A Nat cannot represent $n") 

Stick to simpler things

This, however, is rather cumbersome, because whatever approach you choose, it will rely on the macro, and macros cannot work if the value is not known at compile time . This can become very limited if the easier delegation method no longer works.

In practice, it may be more efficient to use this approach and use conventional methods. Regardless of whether you use the formless or sophisticated library mentioned above, the story does not change, so for normal use it is probably cleaner to perform a validation check.

+2
source

You will need a refined library. This is the only way without resorting to Nat or other types of tricks. From an example in README:

 import eu.timepit.refined._ import eu.timepit.refined.api.Refined import eu.timepit.refined.auto._ import eu.timepit.refined.numeric._ // This refines Int with the Positive predicate and checks via an // implicit macro that the assigned value satisfies it: scala> val i1: Int Refined Positive = 5 i1: Int Refined Positive = 5 // If the value does not satisfy the predicate, we get a meaningful // compile error: scala> val i2: Int Refined Positive = -5 <console>:22: error: Predicate failed: (-5 > 0). val i2: Int Refined Positive = -5 
+8
source

All Articles