How to transform a type?

I’d like to associate two types with each other, so that when I have some type In, I can call a method or a macro and get out some object of type Out, where Out type depends on In:

def transformType[In]: Out = ???

I can get something very similar easily with implicits only and it works fine, so the compiler generally knows how to do what I want, assuming the association is unique by only one available instance of the implicit:

def transform[In, Out](in: In)(implicit obj: Transform[In, Out]): Out = ???

implicit object IntTransform extends Transform[Int, String]
val out = transform(1)   // out is a String, and the compiler figured it out by itself, awesome!

Unfortunately this has one minor downside - it requires an instance of type In.

Question 1:
Given availability of an implicit associating types In and Out, is it possible to get type Out from type In, inside a macro?

Question 2:
Is it possible to make a method / macro computing such dependent type (dependent on another type, not value)?

val out = transformType[Int]  // out is some default instance of String

A general remark (that might solve your problem): a value of type In is not required in order to have implicit search solve dependent types for you. You can improve on your solution a little bit like this:

trait Transform[In] { type Out }
implicit object IntTransform extends Transform[Int] { type Out = String }

def transform[In](implicit t: Transform[In]): t.Out = ???

def out = transform[Int] 
def foo: String = out   //works!

As for your questions:

  1. There’s a method c.inferImplicitValue. I guess you could use that for this purpose, if you really want to.
  2. You can use WeakTypeTag to inspect the type parameters of the macro, and with whitebox macros you can refine the return type of your macro. But even if you have to use a macro, it’s actually still preferred* to do the type computations with an implicit instead, and use a blackbox macro.

*IDE’s like blackbox macros better, among other things

1 Like

Cool, awesome! Looks like it works, even with nested types.
One more thing - can I also inspect the type member of an implicit resolved inside of a macro? In this example above, will I be able to get the actual type under Out?

There might be a better way, but something like this seems to do the trick:

def macroImpl(c: Context): c.Tree = {
  import c.universe._
  val tree = c.inferImplicitValue(typeOf[Transform[Int]])
  val tpe = tree.tpe.member(TypeName("Out")).asType.toType.dealias
  Literal(Constant(tpe.toString)) 
}

def foo: String = macro macroImpl

assert(foo == "String")
1 Like