Scala macro - Infer implicit value using `c.prefix` - scala

c.inferImplicitValue infers implicit values in the call site scope. Is it possible to infer implicits using the c.prefix scope?
This is not valid code, but expresses what I need:
c.prefix.inferImplicitValue
I'm currently using a naive implementation for this purpose[1], but it has some limitations like not inferring implicit values from defs and detecting duplicated/ambiguous implicit values.
[1] https://github.com/getquill/quill/blob/9a28d4e6c901d3fa07e7d5838e2f4c1f3c16732b/quill-core/src/main/scala/io/getquill/util/InferImplicitValueWithFallback.scala#L12

Simply generating a block with an appropriate (local) import followed by a call to implicitly does the trick:
q"""{import ${c.prefix}._; _root_.scala.Predef.implicitly[$T] }
Where T is an instance of Type representing the type of the implicit value to lookup.
To check if the implicit lookup actually succeeded, you can call Context.typeCheck with silent=true and check if the resulting tree is empty or not.
As an illustration, here is an example that implements an infer method returning None if the implicit was not found in the members of the target object, and otherwise wraps the result in a Some.
import scala.reflect.macros.Context
import scala.language.experimental.macros
def inferImplicitInPrefixContext[T:c.WeakTypeTag](c: Context): c.Tree = {
import c.universe._
val T = weakTypeOf[T]
c.typeCheck(
q"""{
import ${c.prefix}._
_root_.scala.Predef.implicitly[$T]
}""",
silent = true
)
}
def infer_impl[T:c.WeakTypeTag](c: Context): c.Expr[Option[T]] = {
import c.universe._
c.Expr[Option[T]](
inferImplicitInPrefixContext[T](c) match {
case EmptyTree => q"_root_.scala.None"
case tree => q"_root_.scala.Some($tree)"
}
)
}
trait InferOp {
def infer[T]: Option[T] = macro infer_impl[T]
}
Let's test it:
object Foo extends InferOp {
implicit val s = "hello"
}
Foo.infer[String] // res0: Some[String] = Some(hello)
Foo.infer[Int] // res1: None.type = None
implicit val lng: Long = 123L
Foo.infer[Long] // res2: Some[Long] = Some(123)

Related

Obtaining the realised type for a Scala Class using macros

I have some problems with scala macros and identifying the realised type of a constructor.
Not sure if i am doing something wrong here or what the correct call would be.
From the documentation, it looks like typeSignatureIn should return the correct information, e.g. ClassTag[Int], but when I run the macro, I actually get ClassTag[U] which fails to compile as U is the type parameter, rather than the realised type.
import scala.language.experimental.macros
import scala.reflect.ClassTag
import scala.reflect.macros.Context
def macroImpl[T: c.WeakTypeTag](c: Context) = {
import c.universe._
val typeToMock = weakTypeOf[T]
val primaryConstructorOpt = typeToMock.members.collectFirst {
case method: MethodSymbolApi if method.isPrimaryConstructor => method
}
val constructorArgumentsTypes = primaryConstructorOpt.map {
constructor =>
val constructorTypeContext = constructor.typeSignatureIn(typeToMock)
val constructorArguments = constructor.paramss
constructorArguments.map { symbols =>
symbols.map(_.typeSignatureIn(constructorTypeContext))
}
}
println(typeToMock)
println(constructorArgumentsTypes)
c.literalUnit
}
def foo[T] = macro macroImpl[T]
class Foo[U: ClassTag]
foo[Foo[Int]]
running it:
scala> foo[Foo[Int]]
Foo[Int]
Some(List(List(), List(scala.reflect.ClassTag[U]))
I need to get the ClassTag[Int] somehow to be able to generate the correct Tree later, any ideas?
Try using dealias at each place you resolve a type. Scala keeps references in place, even when it knows to what they refer. dealias gets you a copy (?) of the type that has the references replaced.
You should also choose either blackbox or whitebox macros, rather than just scala.reflect.macros.Context.
This seems to work:
import scala.language.experimental.macros
import scala.reflect.ClassTag
import scala.reflect.macros.whitebox.Context
def macroImpl[T: c.WeakTypeTag](c: Context) = {
import c.universe._
val typeToMock = weakTypeOf[T].dealias
val primaryConstructorOpt = typeToMock.members.collectFirst {
case method: MethodSymbolApi if method.isPrimaryConstructor => method
}
val constructorArgumentsTypes = primaryConstructorOpt.map { constructor =>
val constructorTypeContext = constructor.typeSignatureIn(typeToMock).dealias
val constructorArguments = constructorTypeContext.paramLists
constructorArguments.map { symbols =>
symbols.map(_.typeSignatureIn(constructorTypeContext).dealias)
}
}
println(typeToMock)
println(constructorArgumentsTypes)
q"()"
}
def foo[T]: Any = macro macroImpl[T]
class Foo[U: ClassTag]
foo[Foo[Int]]
Result
Foo[Int]
Some(List(List(), List(scala.reflect.ClassTag[Int])))

Generate case classes serializer and deserializer implicitly using play-json

I'm using play-json to map Json to case classes or enums. I'm looking for a smart way of creating Formats implicitly, since my project contains many types definitions.
At the moment I created a simple function to generate Formats for Enums:
def formatEnum[E <: Enumeration](enum: E) = Format(Reads.enumNameReads(enum), Writes.enumNameWrites)
But it takes a non-implicit argument so it cannot be used as implicit converter.
I tried to do the same for case classes:
implicit def caseFormat[A] = Json.format[A]
But I get the error "No unapply or unapplySeq function found", since Json.format is a macro which inspect the structure of the class.
Then I tried to create my macro in this way:
import scala.language.experimental.macros
import scala.reflect.macros.whitebox.Context
implicit def caseFormat[A](): Format[A] = macro impl[A]
def impl[A: c.WeakTypeTag](c: Context)(): c.Expr[Reads[A]] = {
import c.universe._
val TypeRef(pre, sym, args) = weakTypeTag[A].tpe
val t = args.head
val expr = q"Json.format[$t]"
c.Expr[Reads[A]](expr)
}
But the compiler does not find the implicit Format, though there is an implicit def that should generate the value.
Of course I can simply define many implicit val, but I think there is a smarter way to do it.
Assuming you have lots of case classes and you wish to json serialize it on the fly without having to write a play-json writer.
import play.api.libs.json._
import scala.reflect.runtime.{universe => ru}
implicit class writeutil[T: ru.TypeTag](i: T) {
implicit val writer = Json.writes[T]
def toJson() = Json.toJson(i)
}
def toInstance[T: ru.TypeTag](s: String): Option[T] = {
implicit val reader = Json.reads[T]
Json.fromJson[T](Json.parse(s)) match {
case JsSuccess(r: T, path: JsPath) => Option(r)
case e: JsError => None
}
}
An optimal implementation would be to re-use the reader/writer by caching and lookup. You can also read more about play-json.
You can use this as:
case class Entity(a: String, b: Int)
val e = Entity("Stack", 0)
e.toJson()

Scala - implicit macros & materialisation

The use cases for implicit macros is supposed to be the so-called "materialisation" of type class instances.
Unfortunately, the example in the documentation is a bit vague on how that is achieved.
Upon being invoked, the materializer can acquire a representation of T and generate the appropriate instance of the Showable type class.
Let's say I have the following trait ...
trait PrettyPrinter[T]{
def printed(x:T) : String
}
object PrettyPrinter{
def pretty[T](x:T)(implicit pretty:PrettyPrinter[T]) = pretty printed x
implicit def prettyList[T](implicit pretty :PrettyPrinter[T]) = new PrettyPrinter[List[T]] {
def printed(x:List[T]) = x.map(pretty.printed).mkString("List(",", ",")")
}
}
and three test classes
class A(val x:Int)
class B(val x:Int)
class C(val x:Int)
Now I understand that instead of writing the following boilerplate
implicit def aPrinter = new PrettyPrinter[A] {def printed(a:A) = s"A(${a.x})"}
implicit def bPrinter = new PrettyPrinter[B] {def printed(b:B) = s"B(${b.x})"}
implicit def cPrinter = new PrettyPrinter[C] {def printed(c:C) = s"C(${c.x})"}
we should be able to add
implicit def materialise[T] : PrettyPrinter[T] = macro implMaterialise[T]
def implMaterialise[T](c:blackbox.Context):c.Expr[PrettyPrinter[T]] = {
import c.universe._
???
}
to the object PrettyPrinter{...} which then generates the corresponding PrettyPrinters on demand ... how? How do I actually get that "representation of T"?
If I try c.typeOf[T], for example, "No TypeTag available for T".
UPDATE
Trying to use class tags doesn't seem to work either.
implicit def materialise[T:ClassTag] : PrettyPrinter[T] = macro implMaterialise[T]
def implMaterialise[T:ClassTag](c:blackbox.Context):c.Expr[PrettyPrinter[T]] = {
import c.universe._
???
}
results in
Error:(17, 69) macro implementations cannot have implicit parameters other than WeakTypeTag evidences
implicit def materialise[T:ClassTag] : PrettyPrinter[T] = macro implMaterialise[T]
^
update2
Interestingly, using WeakTypeTags doesn't really change anything as
implicit def materialise[T:WeakTypeTag]: PrettyPrinter[T] = macro implMaterialise[T]
def implMaterialise[T](c:blackbox.Context)(implicit evidence : WeakTypeTag[T]):c.Expr[PrettyPrinter[T]]
= {
import c.universe._
???
}
will result in
Error:(18, 71) macro implementations cannot have implicit parameters other than WeakTypeTag evidences
implicit def materialise[T:WeakTypeTag]: PrettyPrinter[T] = macro implMaterialise[T]
^
How do I actually get that "representation of T"?
You need to use c.WeakTypeTag, as hinted at by the compiler message you found in your "UPDATE" section.
This project has a working example that you can adapt: https://github.com/underscoreio/essential-macros/blob/master/printtype/lib/src/main/scala/PrintType.scala
object PrintTypeApp extends App {
import PrintType._
printSymbol[List[Int]]
}
import scala.language.experimental.macros
import scala.reflect.macros.blackbox.Context
import scala.util.{ Try => ScalaTry }
object PrintType {
// Macro that generates a `println` statement to print
// declaration information of type `A`.
//
// This only prints meaningful output if we can inspect
// `A` to get at its definition:
def printSymbol[A]: Unit =
macro PrintTypeMacros.printTypeSymbolMacro[A]
}
class PrintTypeMacros(val c: Context) {
import c.universe._
def printTypeSymbolMacro[A: c.WeakTypeTag]: c.Tree =
printSymbol(weakTypeOf[A].typeSymbol, "")
}

Calling function with implicit parameter from quasioquote

For some reason every time I try to call a function with an implicit parameter from quasiquotes, it fails with
Can't unquote x.universe.Tree, consider providing an implicit instance of Liftable[x.universe.Tree]
What's wrong with that? Am I not allowed to do that? I couldn't find anywhere where it says I can't do it
import scala.language.experimental.macros
import scala.reflect.macros.whitebox
object Foo {
def foo: Unit = macro fooImpl
def fooImpl(c: whitebox.Context): c.Expr[Unit] = {
import c.universe._
implicit val x = c
val res = q"$sysoutFQN"
c.Expr(res)
}
def sysoutFQN(implicit c: whitebox.Context): c.universe.Tree = {
import c.universe._
q"java.lang.System.out.println()"
}
}
because path depend so c.universe.Tree != x.universe.Tree
so you need write x type
implicit val x: c.type = c

Fundep materialization with implicits loosing type information

I am trying to use fundep materialization to add compile time safety. The return type of the macro is dependent on the string which is passed into the macro (i.e. the implicit in Foo.scala).
My end goal is a DSL which looks something like this,
"Eggs, Bacon".shoppingList.order
with methods such as order having implicit parameters using the HList which is generated by the string "Eggs, Bacon" in the macro.
I am seeing some strange behaviour in which,
Calling the macro explicitly means I can't call the method on the returned object inline
If I rely on the implicit conversion which uses the fundep method, the type is erased and I seem to be casting to types without ever explicitly casting
Materializing explicitly and then calling a method is OK.
Foo.scala has the simplified macro and FooExample.scala has the strange behaviour.
Is there anyway to do this?
Foo.scala
import shapeless._
import scala.language.experimental.macros
import scala.language.implicitConversions
import scala.reflect.macros.whitebox
class Foo[L <: HList](val list: L) { self =>
def foo: Foo[L] = self
}
object Foo {
implicit def materializeFoo[L <: HList](foo: String): Foo[L] = macro FooMacro.materialize_foo_impl[L]
}
object FooMacro {
def materialize_foo_impl[L <: HList : c.WeakTypeTag](c: whitebox.Context)(foo: c.Expr[String]): c.Tree = {
import c.universe._
q"""{ new Foo($foo :: HNil) }"""
}
}
FooExample.scala
import shapeless._
import shapeless.test._
import Foo._
final class Bar[L <: HList](foo: Foo[L]) {
def bar: L = foo.list
}
object FooExample {
implicit def foo2Bar[L <: HList](foo: Foo[L]): Bar[L] = new Bar(foo)
def main(args: Array[String]) {
val expected = "test" :: HNil
val notExpected = HNil
val f1 = Foo.materializeFoo("test")
sameTyped(expected)(f1.list)
val f2 = "test".foo
sameTyped(f2.list)(expected)
sameTyped(f2.list)(notExpected)
val l1: String :: HNil = f2.list
//This actually compiles but throws a ClassCastException
//val l2: HNil = f2.list
illTyped("""Foo.materializeFoo("test").list""")
sameTyped(expected)("test".foo.list)
sameTyped(notExpected)("test".foo.list)
sameTyped(expected)(f1.bar)
illTyped("""Foo.materializeFoo("test").bar""")
//I actually just want to call "test".foo.bar and have bar have the correct type
}
}