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, "")
}
Related
I have a parameterized class like
class Test[T]{
//...
}
object Test{
implicit def materializeTest[T]: Test[T] = macro impl[T]
def impl[T: c.WeakTypeTag](c: blackbox.Context) = //...
}
If using the materialized implicit from the same module it throws an error:
macro implementation not found
But the problem is extracting a single class into a separate module looks absolutely ugly and cumbersome. Maybe there is some "well-known workaround" to avoid that? Maybe shapeless can be helpful here?
UPD:
scalaVersion in ThisBuild := "2.13.2"
Here is my minimal example:
import scala.language.experimental.macros
import scala.reflect.macros.blackbox
object Main {
sealed trait Adt
case object Adt1 extends Adt
case object Adt2 extends Adt
trait Test[Adtt <: Adt] {
def restrict(restrictions: List[Int]): List[Int]
}
object Test {
def apply[Adtt <: Adt](implicit ev: Test[Adtt]): Test[Adtt] = ev
implicit def implicitMaterializer[
Adtt <: Adt
]: Test[Adtt] = macro impl[Adtt]
def impl[Adtt <: Adt: c.WeakTypeTag](
c: blackbox.Context
): c.Expr[Test[Adtt]] = {
import c.universe._
c.Expr[Test[Adtt]](q"""???""")
}
}
def main(args: Array[String]): Unit = {
Test[Adt1.type].restrict(List(1, 2, 3))
}
}
which results in the following error:
[error] Main.scala:32:9: macro implementation not found: implicitMaterializer
[error] (the most common reason for that is that you cannot use macro implementations in the same compilation run that defines them)
You can extract to a separate module not Test but TestMacro
core
import scala.language.experimental.macros
class Test[T]
object Test {
implicit def materializeTest[T]: Test[T] = macro TestMacro.impl[T]
}
implicitly[Test[Int]] // compiles
macros
import scala.reflect.macros.blackbox
object TestMacro {
def impl[T: c.WeakTypeTag](c: blackbox.Context) = {
import c.universe._
q"new Test[${weakTypeOf[T]}]"
}
}
Ugly or not but macro implementations must be compiled before they are applied (but Is there any trick to use macros in the same file they are defined?).
This is improved in Scala 3
http://dotty.epfl.ch/docs/reference/metaprogramming/macros.html#defining-a-macro-and-using-it-in-a-single-project
Shapeless just hides some predefined set of standard macros, it can't help with your own macros.
Consider a trait that performs "encoding" of arbitrary objects:
trait Encoder[R] {
def encode(r: R): Array[Byte]
}
Assuming encoding of primitive types is known and custom types can be encoded by defining up a "serializer":
trait Serializer[T] {
def serialize(r: T): Array[Byte]
}
we can implement a macro for case class encoding by simply looping the fields and looking up type serializers implicitly. Here's a dummy implementation that looks up the serializer for R itself (in reality we look up serializers for case class field types):
object Encoder {
implicit def apply[R <: Product]: Encoder[R] = macro applyImpl[R]
def applyImpl[R: c.WeakTypeTag](c: blackbox.Context): c.Expr[Encoder[R]] = {
import c.universe._
c.Expr[Encoder[R]](q"""
new ${weakTypeOf[Encoder[R]]} {
override def encode(r: ${weakTypeOf[R]}): Array[Byte] =
implicitly[_root_.Serializer[${weakTypeOf[R]}]].serialize(r)
}
""")
}
}
Now define a base "processor":
abstract class BaseProcessor[R: Encoder] {
def process(r: R): Unit = {
println(implicitly[Encoder[R]].encode(r).length)
}
}
And try to use it:
case class Record(i: Int)
object Serializers {
implicit def recordSerializer: Serializer[Record] =
(r: Record) => Array.emptyByteArray
}
import Serializers._
class Processor extends BaseProcessor[Record]
This fails to compile with:
// [error] Loader.scala:10:22: could not find implicit value for parameter e: Serializer[Record]
// [error] class Processor extends BaseProcessor[Record]
// [error] ^
// [error] one error found
However the following do compile:
class Processor extends BaseProcessor[Record]()(Encoder[Record]) // Compiles!
object x { class Processor extends BaseProcessor[Record] } // Compiles!
I can't really understand why this happens, looks like it has something to do with the Processor definition being a top level definition, since as soon as I move it inside a class/object everything works as expected. Here's one more example that fails to compile:
object x {
import Serializers._ // moving the import here also makes it NOT compile
class Processor extends BaseProcessor[Record]
}
Why does this happen and is there any way to make it work?
To make it work you can try one of the following:
1) add context bound
implicit def apply[R <: Product : Serializer]: Encoder[R] = macro applyImpl[R]
def applyImpl[R: c.WeakTypeTag](c: blackbox.Context)(serializer: c.Expr[Serializer[R]]): c.Expr[Encoder[R]] = {
import c.universe._
c.Expr[Encoder[R]](q"""
new Encoder[${weakTypeOf[R]}] {
override def encode(r: ${weakTypeOf[R]}): Array[Byte] =
$serializer.serialize(r)
}
""")
}
2) make macro whitebox
3) put implicit instance recordSerializer: Serializer[Record] to the companion object of Record rather than some object Serializers to be imported
4) maybe your example is easier than actual use case but now it seems you don't need macros
implicit def apply[R <: Product : Serializer]: Encoder[R] = new Encoder[R] {
override def encode(r: R): Array[Byte] = implicitly[Serializer[R]].serialize(r)
}
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 generate some implicits via a macro -the condensed version of the macro looks like this:
object Implicits {
def generate(c:Context):c.Expr[Unit]={
import c.universe._
c.Expr[Unit] {
q"""
object Dud{
implicit val p:java.io.File = new java.io.File("/tmp")
def toString():String ={ "Dud here" }
}
import Dud._
"""
}
}
}
I am using the macro:
object ImplicitTest extends App {
def genImplicits = macro Implicits.generate
genImplicits
val f: File = implicitly[File]
println(f)
}
The test bails out complaining that
ImplicitTest.scala could not find implicit value for parameter e: java.io.File
[error] val f: File = implicitly[File]
[error] ^
What am I doing wrong with this macro?
Based on Travis's answer (Thank you) I wrote the macro using macro annotation: Here is the code if it helps someone else -it's proof of concept
#compileTimeOnly("enable macro paradise to expand macro annotations")
class defaultFileMacro extends StaticAnnotation {
def macroTransform(annottees: Any*) = macro DefaultMacro.impl
}
object DefaultMacro {
def impl(c: Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
import c.universe._
val inputs:List[Tree] = annottees.map(_.tree).toList
val tree= inputs(0)
val q"val $list:List[$t]= $files" = tree
print(show(q"""implicit val fl1:$t = $files(0)"""))
c.Expr[Any] {
q"""
implicit val fl1:$t = $files(0)
"""
}
}
}
Usage:
object ImplicitTest extends App {
def findDefaultFile() = {
#defaultFileMacro val list: List[File] = List(new File("/tmp"))
val f: File = implicitly[File]
println(f)
}
findDefaultFile()
}
run
> run-main ImplicitTest
[info] Running ImplicitTest
/tmp
The call to the macro method will be expanded to something like this (I generated this text by printing the expression before returning it, and after adding an override to the toString definition):
{
object Dud extends scala.AnyRef {
def <init>() = {
super.<init>();
()
};
implicit val p: java.io.File = new java.io.File("/tmp");
override def toString(): String = "Dud here"
};
import Dud._;
()
}
Note that this doesn't bring anything into scope in the body of ImplicitTest. Both the Dud object and the import are inside a block, and neither are available by the time you get to the val f: File = implicitly[File] line.
You can write an implicit macro method, or you can write a macro annotation that you could apply to ImplicitTest that would add both Dud and the import, but you can't introduce new objects or imports into scope with a def macro.
This is a follow-up to my previous question:
Suppose I create the following test converter.scala:
trait ConverterTo[T] {
def convert(s: String): Option[T]
}
object Converters {
implicit val toInt: ConverterTo[Int] =
new ConverterTo[Int] {
def convert(s: String) = scala.util.Try(s.toInt).toOption
}
}
class A {
import Converters._
def foo[T](s: String)(implicit ct: ConverterTo[T]) = ct.convert(s)
}
Now when I tried to call foo in REPL it fails to compile:
scala> :load converter.scala
Loading converter.scala...
defined trait ConverterTo
defined module Converters
defined class A
scala> val a = new A()
scala> a.foo[Int]("0")
<console>:12: error: could not find implicit value for parameter ct: ConverterTo[Int]
a.foo[Int]("0")
^
import Converters._ in class A does not cut it. You can remove it and the code will still compile. The moment the compiler needs to find in actual implicit is not in class A, where foo is just declared.
The compiler needs to find a ConverterTo[Int] in implicit scope at the moment you call a.foo[Int](..) that is in the REPL. So this is where the import needs to be.
Had object Converters and trait ConverterTo been named the same (so there would be a companion object) the import would not be needed.