Scala 3 tuple + inlne: why is the value not assumed constant?

I have been experimenting again with tuples and tried to select an item from a tuple indexed by a string. If the index does not exist, this should fail at compile time. I am assuming tuples of arbitrary length (possibly > 22). Here is an example of such a tuple:

 (("a",1), ("b",2))

So if I select by “a”, I should get a 1. I have come up with the function bellow that seems to compile, however its application fails irrespective of whether or not the key exists. The error is:

Cannot reduce `inline if` because its condition is not a constant value: 

Here is the code:

import scala.compiletime.*

inline def find5[ID <: String, Labels <: Tuple](id: ID, labels: Labels)(using Tuple.Union[Labels] <:< Tuple2[String,Int]): Int =
    inline erasedValue[Labels] match {
      case _: EmptyTuple => 
        error("No ID found 5")
      case _: ((head,idx) *: tail) => 
        val v = erasedValue[head]
        inline if v == erasedValue[ID]
          val vtail = labels.drop(1).asInstanceOf[tail]
          find5[ID, tail](id, vtail)(using summonInline[Tuple.Union[tail] <:< Tuple2[String,Int]])

// error: Cannot reduce `inline if` because its condition is not a constant value: v.==(scala.compiletime.erasedValue[String]) 
val rf9 = find5("a", (("a",1), ("b",2)))

Here is the working code:

Is what I am attempting impossible? Will I have to resort to recursive type matching via implicits as discussed here?


You can’t use erasedValue as a value.

scala> inline def test[A] = { val a = erasedValue[A] }
def test[A]: Unit

scala> test[Int]
-- Error: ----------------------------------------------------------------------
1 |test[Int]
  |method erasedValue is declared as `erased`, but is in fact used

@kavedaa thanks for taking a stab at it, but that is not the issue. I am using value types. So in your example, test[1] would work.

I can also change the example above to work, if I do not pass in parameters. See link below:

I believe this has something to do with type inference.

Assume I have a class A and B extends A. If I have a method func[T<: A](a:<T) and I call func(b:B), I expect the type T to be bound to the most specific type B. In other words:

func(b:B) =:= func[B](b:B)

This does not seem to happen. In the link below, I show how if I add all value types, the code works.


I think this typing issue is somehow connected to the Tuple class. In the link below I have added a simple check on the func(b:B) =:= func[B](b:B), which seems to work: