Get sequence of types from HList in macro

87 Views Asked by At

Context: I'm trying to write a macro that is statically aware of an non-fixed number of types. I'm trying to pass these types as a single type parameter using an HList. It would be called as m[ConcreteType1 :: ConcreteType2 :: ... :: HNil](). The macro then builds a match statement which requires some implicits to be found at compile time, a bit like how a json serialiser might demand implicit encoders. I've got a working implementation of the macro when used on a fixed number of type parameters, as follows:

def m[T1, T2](): Int = macro mImpl[T1, T2]

def mImpl[T1: c.WeakTypeTag, T2: c.WeakTypeTag](c: Context)(): c.Expr[Int] = {
  import c.universe._
  val t = Seq(
    weakTypeOf[T1],
    weakTypeOf[T2]
  ).map(c => cq"a: $c => externalGenericCallRequiringImplicitsAndReturningInt(a)")
  val cases = q"input match { case ..$t }"
  c.Expr[Int](cases)
}

Question: If I have a WeakTypeTag[T] for some T <: HList, is there any way to turn that into a Seq[Type]?

def hlistToSeq[T <: HList](hlistType: WeakTypeTag[T]): Seq[Type] = ???

My instinct is to write a recursive match which turns each T <: HList into either H :: T or HNil, but I don't think that kind of matching exists in scala.

I'd like to hear of any other way to get a list of arbitrary size of types into a macro, bearing in mind that I would need a Seq[Type], not Expr[Seq[Type]], as I need to map over them in macro code.

A way of writing a similar 'macro' in Dotty would be interesting too - I'm hoping it'll be simpler there, but haven't fully investigated yet.

Edit (clarification): The reason I'm using a macro is that I want a user of the library I'm writing to provide a collection of types (perhaps in the form of an HList), which the library can iterate over and expect implicits relating to. I say library, but it will be compiled together with the uses, in order for the macros to run; in any case it should be reusable with different collections of types. It's a bit confusing, but I think I've worked this bit out - I just need to be able to build macros that can operate on lists of types.

1

There are 1 best solutions below

4
Dmytro Mitin On BEST ANSWER

Currently you seem not to need macros. It seems type classes or shapeless.Poly can be enough.

def externalGenericCallRequiringImplicitsAndReturningInt[C](a: C)(implicit 
  mtc: MyTypeclass[C]): Int = mtc.anInt

trait MyTypeclass[C] {
  def anInt: Int
}
object MyTypeclass {
  implicit val mtc1: MyTypeclass[ConcreteType1] = new MyTypeclass[ConcreteType1] {
    override val anInt: Int = 1
  }

  implicit val mtc2: MyTypeclass[ConcreteType2] = new MyTypeclass[ConcreteType2] {
    override val anInt: Int = 2
  }

  //...
}

val a1: ConcreteType1 = null
val a2: ConcreteType2 = null
externalGenericCallRequiringImplicitsAndReturningInt(a1) //1
externalGenericCallRequiringImplicitsAndReturningInt(a2) //2