Macro to map "items" to their type parameterized "containers" -- large compiler error - scala

Suppose I have a trait for "items" and another trait for "containers" of those items:
sealed trait Item
case class Marble() extends Item
case class Book() extends Item
sealed trait Container[Item]
case object Pouch extends Container[Marble]
case object Shelf extends Container[Book]
Using the type information above, I'd like to construct a map like so via a macro and some runtime reflection:
// Map(classOf[Marble] -> Pouch, classOf[Book] -> Shelf)
val itemToContainer = typeParamMap[Container[_], Item](Pouch, Shelf)
Here's my attempt, which compiles fine, and works in an intermediate form that just returns the list of TypeTags:
object Macros {
def getTag[T](implicit ttag: ru.TypeTag[T]) = ttag
def getBaseParam(child: ru.Type, base: ru.Symbol, paramBase: ru.Symbol) =
child.baseType(base)
.find(_.baseClasses.contains(paramBase))
.get
.typeSymbol
.asClass // TODO how to get java.lang.Class from here?
def typeParamMapImpl[B, P](c: Context)(children: c.Expr[B]*)(baseTag: c.Expr[ru.WeakTypeTag[B]], paramTag: c.Expr[ru.WeakTypeTag[P]]) = {
import c.universe._
val getTagTree = Select(Ident(newTermName("Macros")), "getTag")
val typeTags = c.Expr[List[ru.TypeTag[_]]](Apply(reify(List).tree, children.map( t =>
c.Expr(TypeApply(getTagTree, List(t.tree))).tree
).toList))
reify {
typeTags.splice.map { child =>
getBaseParam(child.tpe, baseTag.splice.tpe.typeSymbol, paramTag.splice.tpe.typeSymbol) -> child.tpe
}.toMap
}
}
def typeParamMap[B, P](children: B*)(implicit baseTag: ru.WeakTypeTag[B], paramTag: ru.WeakTypeTag[P]) = macro Macros.typeParamMapImpl[B, P]
}
However when I try to compile with an invocation of this, I get a huge compiler error: https://gist.github.com/4647812
Can anyone spot the problem, or suggest a better way of going about this?
Thanks!

Related

How to match a Case Class containing a Parameter with Generic Type

I have an interesting Problem matching a Case Class in Scala....
I am using Akka and I have functionality that I will use in every Actor in my System, so created a Base Class for my Actor and I try to Match that Command there....
My Command looks like the following...
sealed trait ReportCommand extends ProcessCommand
final case class onReport(key: Key, replyTo: ActorRef[ResponseBase[State]]) extend ReportCommand
while I constructed Base Class so that it might be used from different Actors, onReport is delivered to Base Actor as generic parameter to be used in pattern match with a case class ...
abstract class BaseActor[E: ClassTag, R <: ReportBase[STATE], COMMAND](signal: TypeCase[R]) {
private val report = signal
def base[B <: E: ClassTag](cmd: E, state: STATE)(f: B => ReplyEffect[COMMAND, STATE]): ReplyEffect[COMMAND, STATE] =
cmd match {
case report(report) =>
Effect.reply(report.replytTo)(new ResponseBase[STATE]{
override def state: STATE = state
})
}
}
First if you think this construct will not work, it works, I have another Command (which I didn't place here) which does not have a generic parameter in the Command Class and above snippet is able to match that Snippet.
Now when I first try this code, Shapeless is complained about there is no mapping to ActorRef for Typeable of TypeCase, so after researching the internet I found I have to do the following....
implicit def mapActorRef[T: ClassTag]: Typeable[ActorRef[T]] =
new Typeable[ActorRef[T]] {
private val typT = Typeable[T]
override def cast(t: Any) : Option[ActorRef[T]] = {
if(t==null) None
else if(t.isInstanceOf[ActorRef[_]]) {
val o= t.asInstanceOf[ActorRef[_]]
for {
_ <- typT.cast(myClassOf)
} yield o.asInstanceOf[ActorRef[T]]
} else None
}
}
def myClassOf[T: ClassTag] = implicitly[ClassTag[T]].runtimeClass
implicit def responseBaseIsTypeable[S: Typeable] : Typeable[ResponseBase[S]] =
new Typeable[ResponseBase[S]] {
private val typS = Typeable[S]
override def cast(t: Any) : Option[ResponseState[S]] = {
if(t==null) None
else if(t.isIntanceOf[ResponseBase[_]]) {
val o = t.asInstanceOf[ResponseBase[_]]
for {
_ <- typS.cast(o.state)
} yield o.asInstanceOf[ResponseBase[S]]
} else None
}
}
Now after this changes I don't receive any Exceptions from Shapeless but case report(report) is not matching, I have no idea how we get a reasoning from Scala why it decide it does not match. During my debugging session I observed the following.
I am using the Akka's Ask Pattern to communicate with this actor...
val future : Future[BaseActor.ResponseBase[Actor.State]] = actorRef.ask[BaseActor.ResponseBase[Actor.State]](ref =>
Actor.onReport(key, ref)
)
now if I observe the cmd object that BaseActor receives, I see that 'ask' Pattern of the Akka change ActorRef in the onReport Command class to an ActorRefAdapter, of course ActorRefAdapter is a subclass of an ActorRef but I am not sure what I defined in the implicit for mapping ActorRef to TypeCase can deal with that stuff but I can't figure a way to change implicit to be aware of the Subtypes....
Unfortunately ActorRefAdapter is private to package package akka.actor.typed.internal.adapter so I can't define an extra mapping for ActorRefAdapter.
So can anybody see why Scala is not matching over my Shapeless <-> TypeCase configuration and give me some tips...
Thx for answers...
Your instance Typeable[ActorRef[T]] is incorrect.
Why did you decide to substitute a ClassTag in typT.cast(myClassOf)? This can't be meaningful.
I guess you used something like "No default Typeable for parametrized type" using Shapeless 2.1.0-RC2
If your gole is to make case report(replyTo) matching then you can define
implicit def mapActorRef[T: Typeable]: Typeable[ActorRef[T]] =
new Typeable[ActorRef[T]] {
private val typT = Typeable[T]
override def cast(t: Any): Option[ActorRef[T]] = {
if (t == null) None
else util.Try(t.asInstanceOf[ActorRef[T]]).toOption
}
override def describe: String = s"ActorRef[${typT.describe}]"
}
The problem is that this instance is also bad. Now case report(replyTo) is matching too much.
val actorTestKit = ActorTestKit()
val replyToRef = actorTestKit.spawn(ReplyToActor(), "replyTo")
import BaseActor._ // importing implicits
import shapeless.syntax.typeable._
val future: Future[BaseActor.ResponseBase[Actor.State]] = replyToRef.cast[ActorRef[Int]].get.ask[BaseActor.ResponseBase[Actor.State]](ref =>
1
)(5.seconds, system.scheduler)
Await.result(future, 10.seconds) // ClassCastException
A legal instance of the type class Typeable can be defined not for every type.
Providing instances for (concrete instantiations of) polymorphic types (where well defined) is pretty much the whole point of Typeable, both here and in Haskell.
The key phrase in the above is "where well defined". It's well defined in the case of non-empty container-like things. It's clearly not well defined for function values.
https://github.com/milessabin/shapeless/issues/69
ResponseBase is a non-empty container-like thing. But ActorRef is like a function T => Unit, so there shouldn't be a Typeable for it
trait ActorRef[-T] extends ... {
def tell(msg: T): Unit
...
}
You should reconsider your approach.

TypeTag for case classes

I would like to make a case class Bla that takes a type parameter A and it knows the type of A at runtime (it stores it in its info field).
My attempt is shown in the example below. The problem is that this example does not compile.
case class Bla[A] (){
val info=Run.paramInfo(this) // this does not compile
}
import scala.reflect.runtime.universe._
object Run extends App{
val x=Bla[Int]
def paramInfo[T](x:T)(implicit tag: TypeTag[T]): String = {
val targs = tag.tpe match { case TypeRef(_, _, args) => args }
val tinfo=s"type of $x has type arguments $targs"
println(tinfo)
tinfo
}
paramInfo(x)
}
However when I comment val info=Run.paramInfo(this) then the program runs fine and prints:
type of Bla() has type arguments List(Int)
Is there a way to make this example below compile ? (or in some other way achieve the same goal, i.e. that a case class is self aware of the type of it's type parameter?)
There's little point in using reflection based APIs for this, shapeless has a typeclass that exposes compile time information to runtime using an implicit macro.
import shapeless.Typeable
class Test[T : Typeable] {
def info: String = implicitly[Typeable[T]].describe
}
It's also relatively easy to roll your own thing here, with the added inconvenience of having to compile the implicit macro in a different compilation unit than whatever is using it.
You just need to pass the implicit type tag parameter to the case class constructor (otherwise the type information is lost before calling paraInfo which requires it):
case class Bla[A : TypeTag]() { ... }
Which is shorthand for:
case class Bla[A](implicit tag: TypeTag[A]) { ... }

scala typing require implicit

I'm trying to build following
I have a parent generic class
abstract class ResultProvider[+T: Writes](db: DB) {
def get(id: Long): Future[Seq[T]]
}
And some implementations, e.g.
class LengthProvider(db: DB) extends ResultProvider[LengthResult](db){
override def get (userId: Long): Future[Seq[LengthResult]] = ...
}
object LengthProvider extends ((DB) => DisciplinePredictor) {
override def apply(db: DB) = new LengthProvider(db)
}
I have following configuration map:
val providers: Map[String, ((DB) => ResultProvider[???])] = Map(
"length" -> LengthProvider,
"width" -> WidthProvider,
...
)
My question is what should I put in place of ???. Ideally, it should be something like T : Writes, as I only care that this type has Writes implicit implemented, as I'm going to Json.toJson it. It will compile with Any, but then the information that the class should implement Writes implicit is lost.
Or should I use a different approach? I could probably create a superclass for all my result case classes (e.g.LengthResult), but I want to get away with the implicits.
You should be able to write ResultProvider[_] (search for "existential types" if you are unfamiliar with this syntax), but you'll need to give a name to the implicit:
abstract class ResultProvider[+T](db: DB)(implicit val writes: Writes[T]) { ... }
Elsewhere:
val provider: ResultProvider[_] = providers("length")
import provider.writes // makes the implicit visible here
...
You might need to help the compiler (or yourself, if you need to name the type) by providing a type variable:
providers("length") match {
case provider: ResultProvider[a] =>
import provider.writes
...
}

Scala Quasiquotes Destructuring a Type

Context:
I'm working on a library for working with JMX in Scala. One of the objectives is to have a strong typed interface to Managed Beans. I guess akin to to the Spring framework JMX library.
Objective: Macro to Deserialise TabularData to a case class:
// interface for which I'd like to generate an implementation using a macro
trait JMXTabularAssembler[T <: Product] {
def assemble(data: TabularData): T
}
object JMXAnnotations {
case class Attribute(name: String) extends StaticAnnotation
}
case class example(
#Attribute("name") name: String,
#Attribute("age") age: Int,
unmarked: String
)
Problem: There are plenty of examples of composing tree's using the q"" interpolators. But I can't figure out how to use the tq"" interpolator to
extract the fields out of a case class from a type context.
private def mkAssembler[T <: Product : c.WeakTypeTag](c: Context): c.universe.Tree = {
import c.universe._
val tt = weakTypeOf[T]
}
Question: How do I use the QuasiQuote machinery to destructure the fields of my case class so that I can loop over them and filter out fields with my annotation (my Attribute annotation is not available from the approach I am currently taking"). An implementation of the following that returns the fields with annotations in declaration order is what I am after.
private def harvestFieldsWithAnnotations[T<: Product: c.WeakTypeTag](c: Context):
List[(c.universe.Name, String, c.universe.Type, List[c.universe.Annotation])] = ???
Bonus: The objective is to get the attribute fields, generate trees for each field that extract the field from the TabularData and use these trees to create the JMXTabularAssembler Functor. If you could show me how to do this for the example above it would bootstrap my efforts :D.
What I have tried: I started solving the problem by using reflection. This does not seem the right way to do it. Snippets:
...
val dec = tt.decls.sorted
def getFields = dec.withFilter( t=> t.isTerm && ! t.isMethod)
def getCaseAccessors = dec.withFilter( t => t.isMethod && t.asMethod.isCaseAccessor)
dec.foreach { d=>
println(d.name, d.annotations)
}
getFields.foreach { f =>
println(f.annotations)
}
val types = getCaseAccessors.map { d =>
println(d.annotations)
(d.name, tt.member(d.name).asMethod.returnType)
}
...
The following method does the trick, it does not use quasi quotes. The key is to access the backing field of a symbol representing the field accessor of a case class (the accessed call).
private def harvestFieldsWithAnnotations[T <: Product : c.WeakTypeTag](c: Context) = {
import c.universe._
val tt = weakTypeOf[T]
tt.decls.sorted.filter(t => t.isMethod && t.asMethod.isCaseAccessor).map { ca =>
val asMethod = tt.member(ca.name).asMethod
(ca.name, asMethod.returnType, asMethod.accessed.annotations)
}
}
Field annotations won't get retained unless they are explicitly annotated with scala.annotation.meta.field.
So the Attribute annotation should be:
#field
case class Attribute(name: String) extends StaticAnnotation

Introspect argument passed to a Scala macro

I would like to program a Scala macro that takes an instance of a case class as argument. All objects that can be passed to the macro have to implement a specific marker trait.
The following snippet shows the marker trait and two example case classes implementing it:
trait Domain
case class Country( id: String, name: String ) extends Domain
case class Town( id: String, longitude: Double, latitude: Double ) extends Domain
Now, I would like to write the following code using macros to avoid the heaviness of runtime reflection and its thread unsafety:
object Test extends App {
// instantiate example domain object
val myCountry = Country( "CH", "Switzerland" )
// this is a macro call
logDomain( myCountry )
}
The macro logDomain is implemented in a different project and looks similar to:
object Macros {
def logDomain( domain: Domain ): Unit = macro logDomainMacroImpl
def logDomainMacroImpl( c: Context )( domain: c.Expr[Domain] ): c.Expr[Unit] = {
// Here I would like to introspect the argument object but do not know how?
// I would like to generate code that prints out all val's with their values
}
}
The macro's purpose should be to generate code that - at runtime - outputs all values (id and name) of the given object and prints them as shown next:
id (String) : CH
name (String) : Switzerland
To achieve this, I would have to dynamically inspect the passed type argument and determine its members (vals). Then I would have to generate an AST representing the code that creates the log output. The macro should work regardless of what specific object implementing the marker trait "Domain" is passed to the macro.
At this point I am lost. I would appreciate if someone could give me a starting point or point me to some documentation? I am relatively new to Scala and have not found a solution in the Scala API docs or the Macro guide.
Listing the accessors of a case class is such a common operation when you're working with macros that I tend to keep a method like this around:
def accessors[A: u.WeakTypeTag](u: scala.reflect.api.Universe) = {
import u._
u.weakTypeOf[A].declarations.collect {
case acc: MethodSymbol if acc.isCaseAccessor => acc
}.toList
}
This will give us all the case class accessor method symbols for A, if it has any. Note that I'm using the general reflection API here—there's no need to make this macro-specific yet.
We can wrap this method up with some other convenience stuff:
trait ReflectionUtils {
import scala.reflect.api.Universe
def accessors[A: u.WeakTypeTag](u: Universe) = {
import u._
u.weakTypeOf[A].declarations.collect {
case acc: MethodSymbol if acc.isCaseAccessor => acc
}.toList
}
def printfTree(u: Universe)(format: String, trees: u.Tree*) = {
import u._
Apply(
Select(reify(Predef).tree, "printf"),
Literal(Constant(format)) :: trees.toList
)
}
}
And now we can write the actual macro code pretty concisely:
trait Domain
object Macros extends ReflectionUtils {
import scala.language.experimental.macros
import scala.reflect.macros.Context
def log[D <: Domain](domain: D): Unit = macro log_impl[D]
def log_impl[D <: Domain: c.WeakTypeTag](c: Context)(domain: c.Expr[D]) = {
import c.universe._
if (!weakTypeOf[D].typeSymbol.asClass.isCaseClass) c.abort(
c.enclosingPosition,
"Need something typed as a case class!"
) else c.Expr(
Block(
accessors[D](c.universe).map(acc =>
printfTree(c.universe)(
"%s (%s) : %%s\n".format(
acc.name.decoded,
acc.typeSignature.typeSymbol.name.decoded
),
Select(domain.tree.duplicate, acc.name)
)
),
c.literalUnit.tree
)
)
}
}
Note that we still need to keep track of the specific case class type we're dealing with, but type inference will take care of that at the call site—we won't need to specify the type parameter explicitly.
Now we can open a REPL, paste in your case class definitions, and then write the following:
scala> Macros.log(Town("Washington, D.C.", 38.89, 77.03))
id (String) : Washington, D.C.
longitude (Double) : 38.89
latitude (Double) : 77.03
Or:
scala> Macros.log(Country("CH", "Switzerland"))
id (String) : CH
name (String) : Switzerland
As desired.
From what I can see, you need to solve two problems: 1) get the necessary information from the macro argument, 2) generate trees that represent the code you need.
In Scala 2.10 these things are done with the reflection API. Follow Is there a tutorial on Scala 2.10's reflection API yet? to see what documentation is available for it.
import scala.reflect.macros.Context
import language.experimental.macros
trait Domain
case class Country(id: String, name: String) extends Domain
case class Town(id: String, longitude: Double, latitude: Double) extends Domain
object Macros {
def logDomain(domain: Domain): Unit = macro logDomainMacroImpl
def logDomainMacroImpl(c: Context)(domain: c.Expr[Domain]): c.Expr[Unit] = {
import c.universe._
// problem 1: getting the list of all declared vals and their types
// * declarations return declared, but not inherited members
// * collect filters out non-methods
// * isCaseAccessor only leaves accessors of case class vals
// * typeSignature is how you get types of members
// (for generic members you might need to use typeSignatureIn)
val vals = typeOf[Country].declarations.toList.collect{ case sym if sym.isMethod => sym.asMethod }.filter(_.isCaseAccessor)
val types = vals map (_.typeSignature)
// problem 2: generating the code which would print:
// id (String) : CH
// name (String) : Switzerland
//
// usually reify is of limited usefulness
// (see https://stackoverflow.com/questions/13795490/how-to-use-type-calculated-in-scala-macro-in-a-reify-clause)
// but here it's perfectly suitable
// a subtle detail: `domain` will be possibly used multiple times
// therefore we need to duplicate it
val stmts = vals.map(v => c.universe.reify(println(
c.literal(v.name.toString).splice +
"(" + c.literal(v.returnType.toString).splice + ")" +
" : " + c.Expr[Any](Select(domain.tree.duplicate, v)).splice)).tree)
c.Expr[Unit](Block(stmts, Literal(Constant(()))))
}
}