Home > Enterprise >  Scala, enforce length of Array/Collection parameter
Scala, enforce length of Array/Collection parameter

Time:09-17

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.

  1. Can I enforce that myArray.size == 3 at type/compile level?
  2. 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.

  • Related