Alexander Nekrasov
Alexander Nekrasov

Reputation: 19

Limit generic case class parameters with upper bound in scala

I would like to limit parameter function to have parameter only case class with limited field types. Let's say i would allow only Int and String


def apply[T <: ???](t: T) {
   ...
}

case class OneParam(int: Int)
case class TwoParams(int: Int, str: String)
case class CompilationErrorClass(list: Array[String])

val oneParam = OneParam(1)
val twoParams = TwoParams(1, "2")
val compilationErrorClass = CompilationErrorClass(List())

val result1 = apply[OneParam](oneParam)
val result2 = apply[TwoParams](twoParams)
val result3 = apply[CompilationErrorClass](compilationErrorClass) // -- this will not compile as has not allowed upper-bound parameter type

How this trick can be done in scala?

Upvotes: 0

Views: 109

Answers (1)

esse
esse

Reputation: 1551

Here we go (scala3):

import scala.deriving.Mirror.ProductOf

type AllowTypes = Int *: EmptyTuple | String *: EmptyTuple | (Int, String) | (String, Int)

def size[P <: Product](t: P)
                (using p: ProductOf[P],
              ev: p.MirroredElemTypes <:< AllowTypes): Int =
  Tuple.fromProductTyped(t).size


scala> size(OneParam(0))
val res0: Int = 1

scala> size(TwoParams(1, ""))
val res1: Int = 2

scala> size(CompilationErrorClass(Array()))
-- Error: --------------------------------------------------------------------------------------------------------------------------------------------------
1 |size(CompilationErrorClass(Array()))
  |                                    ^
  |                                    Cannot prove that p.MirroredElemTypes <:< AllowTypes.
1 error found

Or even general solution, allow all case classes with arbitrarily int or string arguments:

scala> type Allowed[T <: Tuple] = T match
     |   case EmptyTuple          => DummyImplicit
     |   case (Int | String) *: t => Allowed[t]
     |

scala> import scala.deriving.Mirror.ProductOf
     |
     | def size[P <: Product](t: P)
     |                 (using p: ProductOf[P],
     |                       ev: Allowed[p.MirroredElemTypes]): Int =
     |   Tuple.fromProductTyped(t).size
     |
def size[P <: Product](t: P)(using p: deriving.Mirror.ProductOf[P], ev: Allowed[p.MirroredElemTypes]): Int

scala> case class A(i: Int, j: Int, x: String, y: String)
// defined case class A

scala> case class X(x1: Int, x2: String, x3: Int, x4: String, x5: String)
// defined case class X

scala> case class Y(l: Long)
// defined case class Y

scala> size(A(0, 1, "", ""))
val res0: Int = 4

scala> size(X(0, "", 1, "", ""))
val res1: Int = 5

scala> size(Y(0))
-- Error: --------------------------------------------------------------------------------------------------------------------------------------------------
1 |size(Y(0))
  |    ^
  |    Match type reduction failed since selector  Long *: EmptyTuple.type
  |    matches none of the cases
  |
  |        case EmptyTuple => DummyImplicit
  |        case (Int | String) *: t => Allowed[t]
1 error found

Upvotes: 1

Related Questions