Scala 3: split tuple to exact types

My objective is to flatten a Tuple and get its flattened version with the exact tyoe.
So the following should compie:

    val c1: Tuple4[Int, Int, Char, Char] = flatten( ((1,2), ('a', 'b')) )

I cannot get the types correct. I seem to only get the top type Tuple.
So I experimented with concat to check the typing. The following work correctly:

  def concat[T1 <: Tuple, T2 <: Tuple](t1: T1, t2: T2): Tuple.Concat[T1, T2] = {
    t1 ++ t2
  }

  def concat2[S1 <: Tuple, S2 <: Tuple, T1 <: Tuple, T2 <: Tuple](s1: S1, s2: S2, t1: T1, t2: T2):
  Tuple.Concat[Tuple.Concat[S1, S2], Tuple.Concat[T1, T2]] = {
    val u1 = concat(s1, s2)
    val u2 = concat(t1, t2)
    u1 ++ u2
  }

  def concat2a[S1 <: Tuple, S2 <: Tuple, T1 <: Tuple, T2 <: Tuple](s1: S1, s2: S2, t1: T1, t2: T2):
  Tuple.Concat[Tuple.Concat[S1, S2], Tuple.Concat[T1, T2]] = {
    val u1:Tuple.Concat[S1, S2]  = concat(s1, s2)
    val u2:Tuple.Concat[T1, T2] = concat(t1, t2)
    u1 ++ u2
  }

So on further investigation I tried this:

  def split[S1  <: Tuple, S2 <: Tuple, O <: Tuple](o: O): (S1, S2) = {
    o match {
      case (s@(h1 *: t2 *: _)) *: t =>
        println(s"s = $s")
        println(s"t = $t")
        (s, t)
    }
  }
[error]     |         ^
[error]     |         Found:    (s : Any *: Tuple)
[error]     |         Required: S1
[error]     |
[error]     |         where:    S1 is a type in method split with bounds <: Tuple
[error] -- [E007] Type Mismatch Error: /home/hmf/Test.scala:126:12 
[error] 126 |        (s, t)
[error]     |            ^
[error]     |           Found:    (t : Tuple)
[error]     |           Required: S2
[error]     |
[error]     |           where:    S2 is a type in method split with bounds <: Tuple

One of the issues I see is that the tuple is constructed with a head and tail, and this head need not be a tuple - hence the Any (if this is not so please correct me).

Another issue is that when I pattern match I get general Tuple which does not match a specific <: Tuple.

So my question is, is their any way I can match and retain the most specific type information?

TIA

Investigating this further I tried the use of transparent inlne so:

  transparent inline def split[O <: Tuple](inline o: O) = {
    o match {
      case (s@(_ *: _)) *: t =>
        println(s"s = $s")
        println(s"t = $t")
        (s, t)
    }
  }

And it seems to work: This compiles:

    val (s1: Tuple2[Int, Int], s2: Tuple1[Int]) = split(((1,2),3))

However when I use an inlined match:

  transparent inline def split[O <: Tuple](inline o: O) = {
    inline o match {
      case (s@(_ *: _)) *: t =>
        println(s"s = $s")
        println(s"t = $t")
        (s, t)
    }
  }

I get:

[error] -- Error: /Test.scala:217:55 
[error] 217 |    val (s1: Tuple2[Int, Int], s2: Tuple1[Int]) = split(((1,2),3))
[error]     |                                                  ^^^^^^^^^^^^^^^^
[error]     |cannot reduce inline match with
[error]     | scrutinee:  Tuple2.apply[(Int, Int), Int](Tuple2.apply[Int, Int](1, 2), 3) : ((Int, Int), Int)
[error]     | patterns :  case *:.unapply[Any, Tuple](s @ *:.unapply[Any, Tuple](_, _):Any *: Tuple, t @ _):
[error]     |  Any *: Tuple
[error]     | This location contains code that was inlined from HyperParameters.scala:59
[error] one error found
1 targets failed

I am assuming the value is statically defined during compile time so the match should work. Can anyone explain why it is not so. I suspect this is due to the same issue above.

TIA

I think NonEmptyTuple has the tools to do this.

Couldn’t figure it out. I see an apply, head and tail with corresponding types. I confess when I delved into the run-time code I got lost. I also did not find any uses of matching (TupleXXL is array based and the special cases use access fields).

Also, something else has baffled me in this regard. The class:

sealed abstract class *:[+H, +T <: Tuple] extends NonEmptyTuple

Seems to suggest an HListof sorts, but I find it is only used at the type level (see Tuple.Concat used above). Maybe someone conversant in type level programming + Scala 3 (Dotty) can explain.

Thanks