Scala 3: "Not found: type SizeOf" when using Match Types to map enum size to primitive numeric types

Hi everyone,

I’m working with Scala 3 and attempting to implement a compile-time feature that maps an enum’s total number of members to the smallest appropriate primitive numeric type (or immutable.BitSet as a fallback) using Match Types.

My intended workflow is:

  1. First, calculate the enum’s member count at compile time via the SizeOf inline method (using Mirror.SumOf for compile-time reflection).

  2. Then, use a Match Type (BitOption[E]) to map the SizeOf result to Byte/Short/Int/Long/BitSet based on numeric ranges.

However, when I compile the code, I encounter the error: Not found: type SizeOf — the compiler seems unable to resolve SizeOf as a type within the BitOption Match Type definition.

Here’s my complete, reproducible code:

import scala.collection.immutable
import scala.deriving.Mirror
import scala.compiletime.ops.int.*
import scala.compiletime.*
import scala.reflect.Enum

object Main {

  enum Language:
    case Java, Scala, Kotlin, Groovy, Clojure

  // Compile-time inline method to get enum member count
  transparent inline def SizeOf[E <: Enum](using m: Mirror.SumOf[E]): Int =
    constValue[Tuple.Size[m.MirroredElemTypes]]

  // Match Type to map enum size to corresponding numeric type
  type BitOption[E <: Enum] = SizeOf[E] match
    case 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8    => Byte
    case 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 => Short
    case 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 |
          31 | 32 =>
      Int
    case 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 |
          47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 |
          61 | 62 | 63 | 64 =>
      Long
    case _ => immutable.BitSet

  def main(args: Array[String]): Unit = {
    val usedLanguage: BitOption[Language] = 0
    assert(usedLanguage.isInstanceOf[Byte]) // Expect to pass (Language has 5 members)
  }
}

My specific questions:

  1. Why am I getting the “Not found: type SizeOf” error? I defined SizeOf as a transparent inline method — is there a misunderstanding here about the difference between inline methods and types in Scala 3, especially when using them within Match Types?

  2. How can I correctly reference the compile-time value of SizeOf[E] within the BitOption Match Type to make the compiler resolve it properly?

  3. Additionally, is there a more idiomatic way to express numeric ranges in Scala 3 Match Types (e.g., 1 to 8 instead of listing every number) to avoid verbosity and typos?

Any guidance, corrections, or working examples would be greatly appreciated. Thank you in advance!

The answer is that you can’t call a method from a type definition - there is no concept existing that could allow you to do that.

Can I ask why you thought it should be possible?

You can change the type parameter to BitOption and call it like this:

type BitOption[S <: Int] = ...
def main(args: Array[String]): Unit = {
  val m = Mirror.SumOf[Language]
  val usedLanguage: BitOption[Tuple.Size[m.MirroredElemTypes]] = 0
  assert(usedLanguage.isInstanceOf[Byte]) // Expect to pass (Language has 5 members)
}

you can define a type Range[X, Y] and you can iterate in a loop via recursion and use scala.compiletime.ops.int.S to increment

Thank you for your explanation. It was very helpful. after compiling the code snippet and decompiling the class file using Jadx, I observed that the number of enum members is represented as a constant value in the bytecode. This is why I believe there might be a way to map the enum member count to a primitive data type using Match Types (or a similar compile-time mechanism).

  enum Language:
    case Java, Scala, Kotlin, Groovy, Clojure

  transparent inline def SizeOf[E <: Enum](using m: Mirror.SumOf[E]): Int =
    constValue[Tuple.Size[m.MirroredElemTypes]]

  def main(args: Array[String]): Unit = {
    assert(SizeOf[Language] == 5)
  }

decompiled code:

/* compiled from: Main.scala */
/* loaded from: Main$.class */
public final class Main$ implements Serializable {
    public static final Main$Language$ Language = null;
    public static final Main$ MODULE$ = new Main$();

    private Main$() {
    }

    private Object writeReplace() {
        return new ModuleSerializationProxy(Main$.class);
    }

    public void main(final String[] args) {
        Main$Language$ main$Language$ = Main$Language$.MODULE$;
        if (5 == 5) {
        }
    }
}