[SOLVED] Dotty/ Sala3 macro: how to retain the type of HList elements

I have the following defenition of an HList:

  sealed trait HList
  case class HCons[+HD, +TL <: HList](hd: HD, tl: TL) extends HList
  case object HNil extends HList

I want to use the macros to be able to compile this code:

    type KeyValue = (String, Int)

    val hl2 = HCons("4" -> 4, HCons("5" -> 5, HCons("6" -> 6,HNil)))
    val rhl2: HCons[KeyValue, HCons[KeyValue, HCons[KeyValue, Recorder.HNil.type]]] = reverse(hl2)

However, I cannot seem to keep all required type information. I get the following error:

[error] 291 |    val rhl2: HCons[KeyValue, HCons[KeyValue, HCons[KeyValue, Recorder.HNil.type]]] = reverse(hl2)
[error]     |                                                                                      ^^^^^^^^^^^^
[error]     | Found:    automl.Recorder.HCons[(String, Int), automl.Recorder.HList]
[error]     | Required: automl.Recorder.HCons[KeyValue, 
[error]     |   automl.Recorder.HCons[KeyValue, 
[error]     |     automl.Recorder.HCons[KeyValue, automl.Recorder.HNil.type]
[error]     |   ]
[error]     | ]
[error] one error found

I use the code below:



  private def reverseLoop(e: Expr[HList], acc: Expr[HList])(using qctx:QuoteContext): Expr[HList] = {
    import qctx.reflect._

    e match {
      case '{HCons($a:$H,$t)} =>
        val nacc = '{HCons($a, $acc)}
        reverseLoop(t, nacc)
      case '{HNil} =>
        acc
    }
  }


  transparent inline def reverse(inline expr: HList): Any = {
    ${reverseLoop('expr, 'HNil)}
  }

I Think the problem stems from the fact that I return a generic HList. How does one tackle this problem?

TIA

Bump.

Can anyone lend a a hand?

I have been combing the documentation. What looks most promising is this:

https://dotty.epfl.ch/docs/reference/metaprogramming/macros.html#recovering-precise-types-using-patterns

However I can’t even get this to compile in the latest Scala3 “release” (M1) #10308. <\s>

In my latest iteration I have:

  sealed trait HList
  case class HCons[+HD, TL <: HList](hd: HD, tl: TL) extends HList
  case object HNil extends HList

  private def test1Impl[Xs <: HList, Ys <: HList, Zs <: HList](e: Expr[Xs], acc: Expr[Ys])
                                                 (using xt:Type[Xs], yt:Type[Ys], qctx:QuoteContext):
  Expr[Zs] = {
    import qctx.reflect._

    e match {
      case '{HCons($a, HNil)} =>
        '{HCons($a,$acc)}

      case '{HCons($a,$_)} =>
        '{HCons($a,$acc)}

      case p:Expr[HList] =>
        // passed as an identity, so get underlying value
        val expOk = unsealUnderlying[HList](p)
        // Now deconstruct the value
        test1Impl(expOk, acc)
    }
  }

  inline def test1[Xs <: HList, Ys <: HList](inline xs: Xs ): Ys = {
    ${test1Impl('xs, 'HNil)}
  }

    val t1: HCons[(String, String), HNil.type] = test1(hl3)
    println(t1)

And I get the following error:

[error] 406 |        '{HCons($a,$acc)}
[error]     |          ^^^^^^^^^^^^^^
[error]     |Found:    automl.Recorder.HCons[Any, Ys]
[error]     |Required: Zs
[error]     |
[error]     |where:    Ys is a type in method test1Impl with bounds <: automl.Recorder.HList
[error]     |          Zs is a type in method test1Impl with bounds <: automl.Recorder.HList
[error] -- [E007] Type Mismatch Error: RecordMacro.scala:409:15 
[error] 409 |        '{HCons($a,$acc)}
[error]     |          ^^^^^^^^^^^^^^
[error]     |Found:    automl.Recorder.HCons[Any, Ys]
[error]     |Required: Zs
[error]     |
[error]     |where:    Ys is a type in method test1Impl with bounds <: automl.Recorder.HList
[error]     |          Zs is a type in method test1Impl with bounds <: automl.Recorder.HList

TIA

To solve this we must use Relationship with Whitebox Inline.

Here is working code:

  private def test1Impl[Xs <: HList, Ys <: HList](e: Expr[Xs], acc: Expr[Ys])
                                                 (using xt:Type[Xs], yt:Type[Ys], qctx:QuoteContext):
  Expr[Any] = {
    import qctx.reflect._

    e match {
      case '{HCons($a: (String,String), HNil)} =>
        '{HCons($a,$acc)}

      case '{HCons($a:$t,$_)} =>
        '{HCons($a,$acc)}

      case p:Expr[HList] =>
        // passed as an identity, so get underlying value
        val expOk = unsealUnderlying[HList](p)
        // Now deconstruct the value
        test1Impl(expOk, acc)
    }
  }

  transparent inline def test1[Xs <: HList](inline xs: Xs ): Any = {
    ${test1Impl('xs, 'HNil)}
  }