I am trying to implement a concat
of 2 HList
s. In my first attempt:
private def unsealUnderlying[T](p:Expr[T])(using t:Type[T], qctx:QuoteContext): Expr[T] = {
import qctx.reflect._
val xTree0: Term = p.unseal
xTree0 match {
case Inlined(_, _, _) => // (Inlined(None, Nil, Ident("fta4")))
// passed as an identity, so get underlying value
p.unseal.underlying.seal.cast[T]
case _ =>
report.throwError(s"unsealUnderlying: Expected HList reference but got ${p.show} -- (${p.unseal.showExtractors})", p)
}
}
sealed trait HList
case class HCons[+HD, TL <: HList](hd: HD, tl: TL) extends HList
case object HNil extends HList
private def concatImpl[Xs <: HList, Ys <: HList, Zs <: 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:$t, HNil)} =>
'{HCons($a:t,$acc)}
case '{HCons($a:$t,$tl)} =>
val ntl = concatImpl(tl,acc)
'{HCons($a:t, ${ntl} )}
case p:Expr[Xs] =>
// passed as an identity, so get underlying value
val expOk = unsealUnderlying[Xs](p)
// Now deconstruct the value
concatImpl(expOk, acc)
}
}
transparent inline def concatM[Xs <: HList, Ys <: HList](inline xs: Xs, inline ys: Ys): Any = {
${ concatImpl('xs, 'ys) }
}
I get the error in the second case:
[error] 454 | '{HCons($a:t, ${ntl} )}
[error] | ^^^
[error] | Found: (ntl : quoted.Expr[Any])
[error] | Required: quoted.Expr[automl.Recorder.HList]
[error] one error found
If I return a type HList
I loose the type information of the HList elements. So how can I extract the type of the remainder of the HList
? I then tried to extract and use the type directly from the macro so:
private def concatImpl[Xs <: HList, Ys <: HList, Zs <: 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:$t, HNil)} =>
'{HCons($a:t,$acc)}
case '{HCons($a:$t,$tl:$tlt)} =>
val ntl = concatImpl(tl.asInstanceOf[Expr[HList]],acc)
'{HCons($a:t, ${ntl} )}
case p:Expr[Xs] =>
// passed as an identity, so get underlying value
val expOk = unsealUnderlying[Xs](p)
// Now deconstruct the value
concatImpl(expOk, acc)
}
}
Ok, so I suspect that asInstanceOf
may not be the way to go, however I now get the error:
[error] -- [E007] Type Mismatch Error: /RecordMacro.scala:439:25
[error] 439 | case '{HCons($a:$t,$tl:$tlt)} =>
[error] | ^^^^^^^^
[error] | Found: ev$7.Underlying
[error] | Required: automl.Recorder.HList
[error] -- [E007] Type Mismatch Error: RecordMacro.scala:454:24
[error] 454 | '{HCons($a:t, ${ntl} )}
[error] | ^^^
[error] | Found: (ntl : quoted.Expr[Any])
[error] | Required: quoted.Expr[automl.Recorder.HList]
[error] two errors found
So now I see that the Hlist
tail must have it internals extracted. So now I make another try:
private def concatImpl[Xs <: HList, Ys <: HList, Zs <: 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:$t, HNil)} =>
'{HCons($a:t,$acc)}
case '{HCons($a:$t,$tl)} =>
val xTree0: Term = tl.unseal
val ntl = xTree0 match {
case Inlined(_, _, _) => // (Inlined(None, Nil, Ident("fta4")))
// passed as an identity, so get underlying value
tl.unseal.underlying.seal.cast[HList]
case _ =>
report.throwError(s"unsealUnderlying: Expected HList reference but got (${tl.unseal.showExtractors})", tl)
}
'{HCons($a:t, ${ntl} )}
case p:Expr[Xs] =>
// passed as an identity, so get underlying value
val expOk = unsealUnderlying[Xs](p)
// Now deconstruct the value
concatImpl(expOk, acc)
}
}
and I get the expected error:
[error] |unsealUnderlying: Expected HList reference but got (Apply(TypeApply(Select(Ident("HCons"), "apply"), List(Inferred(), Inferred())), List(Apply(TypeApply(Select(Ident("Tuple2"), "apply"), List(Inferred(), Inferred())), List(Literal(Constant.String("2")), Literal(Constant.Int(2)))), Apply(TypeApply(Select(Ident("HCons"), "apply"), List(Inferred(), Inferred())), List(Apply(TypeApply(Select(Ident("Tuple2"), "apply"), List(Inferred(), Inferred())), List(Literal(Constant.String("3")), Literal(Constant.Double(3.0d)))), Ident("HNil"))))))
[error] | This location contains code that was inlined from HyperParameters.scala:314
Now I see that I have the expected type, but how can I use this in the cast? If I use:
tl.unseal.underlying.seal.cast[HList]
I again end up with a generic HList
. How can I use this information to cast to the proper type?
Appreciate any help. Any pointers to examples or documentation is welcome. I have looked at the TASTy documentation but could not figure this out.
TIA