Suppose I have a class defined as
case class MyClass(myArray: Array[Int])
I want to only allow instances where the myArray parameter is of a specific length, say 3.
- Can I enforce that
myArray.size == 3
at type/compile level? - Would it be different with another collection? (say, an immutable list)
The only way I found to this is via a smart constructor at runtime, checking the size of myArray and failing - using for instance require
.
CodePudding user response:
Can I enforce that myArray.size == 3 at type/compile level?
Yes Scala can represent numbers at type-level. For example, using Sized
from shapeless
import shapeless._
import syntax.sized._
import nat._
case class MyClass(myArray: Sized[Array[Int], _3])
MyClass(Sized[Array](1,2,3)) // ok
MyClass(Sized[Array](1,2,3,4)) // compile-time error
Array(1,2,3,4).sized(3).map(MyClass) // None
Here is how you might go about it in Scala 3 using scala.compiletime.ops
facilities
import scala.compiletime.ops.int.S
enum MyArray[Size, A]:
case Nil extends MyArray[0, Nothing]
case Cons[N <: Int, B](head: B, tail: MyArray[N, B]) extends MyArray[S[N], B]
import MyArray._
val xs: MyArray[3, Int] = Cons(1, Cons(2, Cons(3, Nil)))
val ys: MyArray[4, Int] = Cons(1, Cons(2, Cons(3, Cons(4, Nil))))
case class MyClass(myArray: MyArray[3, Int])
MyClass(xs) // ok
MyClass(ys) // compile-time error
CodePudding user response:
You can use require with everything you want :)
case class MyClass(myArray: Array[Int]) {
require(myArray.size == 3, "Only arrays with 3 items are allowed!")
}
In case you pass an array that does not meet the requirement, it will throw an IllegalArgumentException
.