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
Related
Consider the following two classes, both of them have a parseFrom function.
class X {}
object X {
def parseFrom(b: Array[Byte]): String = "Hello"
}
class Y {}
object Y {
def parseFrom(b: Array[Byte]): String = "HelloY"
}
I want to write a macro:
getParseFrom[X] that will return the X.parseFrom function.
This is what I have so far:
import scala.reflect.macros.whitebox.Context
def getParseFromImpl[T: c.WeakTypeTag](c: Context): c.Tree = {
import c.universe._
val tpe = weakTypeOf[T]
q"""
$tpe.parseFrom(_)
"""
}
def getParseFrom[T]: Array[Byte] => String = macro getParseFromImpl[T]
Is this possible with scala macros in scala 2.12?
Try
q"""
${tpe.typeSymbol.companion}.parseFrom(_)
"""
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, "")
}
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)
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
}
}
I tried to simplify the creation of ASTs, but got a weird error message:
case class Box(i: Int)
object M {
import language.experimental.macros
import scala.reflect.makro.Context
case class meth(obj: String, method: String)(implicit val c: Context) {
import c.universe._
def apply(xs: Tree*) =
Apply(Select(Ident(obj), newTermName(method)), xs.toList)
}
def box(n: Int): Box = macro boxImpl
def boxImpl(c: Context)(n: c.Expr[Int]): c.Expr[Box] = {
import c.universe._
implicit val cc: c.type = c
n.tree match {
case arg # Literal(Constant(_)) =>
meth("Box", "apply").apply(arg)
}
}
}
Error:
<console>:26: error: type mismatch;
found : c.universe.Literal
required: _2.c.universe.Tree where val _2: M.meth
possible cause: missing arguments for method or constructor
meth("Box", "apply").apply(arg)
^
Is it possible to infer the correct types into class meth? Or is there a workaround for the problem?
EDIT: Based on #retronyms answer I got this to work:
object M {
import language.experimental.macros
import scala.reflect.makro.Context
def meth(implicit c: Context) = new Meth[c.type](c)
class Meth[C <: Context](val c: C) {
import c.universe._
def apply(obj: String, method: String, xs: Tree*) =
Apply(Select(Ident(obj), newTermName(method)), xs.toList)
}
def box(n: Int): Box = macro boxImpl
def boxImpl(c: Context)(n: c.Expr[Int]): c.Expr[Box] = {
import c.universe._
implicit val cc: c.type = c
n.tree match {
case arg # Literal(Constant(_)) =>
c.Expr(meth.apply("Box", "apply", arg))
}
}
}
Constructors are not currently allowed to have dependent method types (SI-5712). It's on the radar to be fixed, hopefully for 2.10.1 or 2.11.
In the meantime, you can follow the pattern I used in macrocosm to reuse code within macro implementations.