I am trying to port this answer from this question to scala-2.10.
This is what I tried:
Macros.scala
package myProject.macros
import scala.reflect.macros.Context
import scala.language.experimental.macros
class LoggerImpl(val c: Context) {
import c.universe._
def getClassSymbol(s: Symbol): Symbol = if (s.isClass) s else getClassSymbol(s.owner)
def logImpl(msg: Expr[String]): Unit = {
val cl = getClassSymbol(c.enclosingClass.symbol).toString
// Do something with cl
// For this case cl should be "SomeObject"
}
}
object Logger {
def warning(msg: String): Unit = macro LoggerImpl.logImpl
}
XYZ.scala
package myProject.XYZ
import myProject.macros.Logger
object SomeObject {
def doSomething(...) = {
// Some operations
Logger.warning("sss")
}
}
But when I try to build I get these errors:
[scalac-2.10] /../Macros.scala:20: error: not found: value LoggerImpl
[scalac-2.10] def warning(msg: String): Unit = macro LoggerImpl.logImpl
[scalac-2.10] ^
[scalac-2.10] /../XYZ.scala:18: error: not found: value LoggerImpl
[scalac-2.10] def warning(msg: String): Unit = macro LoggerImpl.logImpl
[scalac-2.10] ^
[scalac-2.10] two errors found
I looked at this example, Isn't there a way to get macros working if they are in different packages?
You could try this:
object LoggerImpl {
def logImpl(c: Context)(msg: c.Expr[String]): c.Expr[Unit] = {
import c.universe._
def getClassSymbol(s: Symbol): Symbol = if (s.isClass) s else getClassSymbol(s.owner)
val cl = getClassSymbol(c.enclosingClass.symbol).toString
// Do something with cl
// For this case cl should be "SomeObject"
}
}
Unfortunately I do not have scala 2.10 at hand, but I suppose the above should work for you, because your code does not compile for scala 2.11 either and the implementations for 10 and 11 do not seem to be to much different
Please note that you need to return c.Expr[Unit] for it to do anything, and you also need to compile it in a separate module - the last is the requirement for scala macros
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.
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, "")
}
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.
I have the following code:
object Macros {
import scala.language.experimental.macros
import scala.reflect.macros.blackbox
def hello(): Unit = macro hello_impl
def hello_impl(c: blackbox.Context)(): c.Expr[Unit] = {
import c.universe._
reify {
println("Hello World!")
}
}
}
object Main {
def main(args: Array[String]): Unit = {
Macros.hello()
}
}
It throws the following compilation error:
Error:(21, 17) macro implementation not found: hello
(the most common reason for that is that you cannot use macro implementations in the same compilation run that defines them)
Macros.hello()
^
My question is: is there a way to "fool" the compiler in order to use macro expansions in the same file they are defined? My motivation is the following: I like to code in Scala, and lately I was submitting some problems in online judge Codeforces and some Scala constructions turned out to be very slow. So, I want to create some macro expansions in order to execute those constructions fast. But I cannot submit more than one file.
Thanks!
At the moment, this is not possible in production releases of Scala 2.10 and 2.11. We might be able to achieve this with scala.meta, but that's well in the future.
Depends on the meaning of "use". In principle you can use a macro (hello) in another method (foo) in the same file if you make the 2nd method a macro too (the 1st method call must be inside a quasiquote q"...", not reify{...})
import scala.language.experimental.macros
import scala.reflect.macros.blackbox // libraryDependencies += scalaOrganization.value % "scala-reflect" % scalaVersion.value
object Macros {
def hello(): Unit = macro hello_impl
def hello_impl(c: blackbox.Context)(): c.Expr[Unit] = {
import c.universe._
reify {
println("Hello World!")
}
}
}
object Main {
def foo(): Unit = macro fooImpl
def fooImpl(c: blackbox.Context)(): c.Tree = {
import c.universe._
q"Macros.hello()"
}
}
https://scastie.scala-lang.org/DmytroMitin/rom8yKvmSUGVk0GwtGzKqQ
The problem is that runnable def main(args: Array[String]): Unit can't be a macro.
Another option is reflective toolbox. A macro can't be defined with toolbox (actual compiler is needed for that)
import scala.reflect.runtime
import scala.reflect.runtime.universe.Quasiquote
import scala.tools.reflect.ToolBox // libraryDependencies += scalaOrganization.value % "scala-compiler" % scalaVersion.value
val rm = runtime.currentMirror
val tb = rm.mkToolBox()
tb.compile(q"""
import scala.language.experimental.macros
import scala.reflect.macros.blackbox.Context
def bar(): Unit = macro barImpl
def barImpl(c: Context)(): c.Tree = {
import c.universe._
EmptyTree
}
""")
//macro implementation reference has wrong shape. required:
//macro [<static object>].<method name>[[<type args>]] or
//macro [<macro bundle>].<method name>[[<type args>]]
But a macro can be run using toolbox
import scala.language.experimental.macros
import scala.reflect.macros.blackbox
object Macros {
def hello(): Unit = macro hello_impl
def hello_impl(c: blackbox.Context)(): c.Expr[Unit] = {
import c.universe._
reify {
println("Hello World!")
}
}
}
import scala.reflect.runtime
import scala.reflect.runtime.universe.Quasiquote
import scala.tools.reflect.ToolBox
object Main {
def main(args: Array[String]): Unit = {
val rm = runtime.currentMirror
val tb = rm.mkToolBox()
tb.eval(q"Macros.hello()")
}
}
// Hello World!
https://scastie.scala-lang.org/DmytroMitin/rom8yKvmSUGVk0GwtGzKqQ/5
Regarding Codeforces the issue is that the compiler must be in classpath and calls via toolbox are comparatively slow.
Possible to identify/use Scala macros using reflection or similar? (Scala 3)
use a macro in the same file as it is defined? it is not possible, even not in Scala 3.
I refer to the description in Scala 3: Defining and Using Macros .
The macro implementation can be in the same project, but not in the same file as the macro usage.
See "Defining and Using Macros" chapter and "Suspended Files" info box.
The following macro is pasted from http://docs.scala-lang.org/overviews/quasiquotes/usecases.html:
import reflect.macros.Context
import language.experimental.macros
val universe = reflect.runtime.universe; import universe._
import reflect.runtime.currentMirror
import tools.reflect.ToolBox
val toolbox = currentMirror.mkToolBox()
object debug {
def apply[T](x: =>T): T = macro impl
def impl(c: Context)(x: c.Tree) = { import c.universe._
val q"..$stats" = x
val loggedStats = stats.flatMap { stat =>
val msg = "executing " + showCode(stat)
List(q"println($msg)", stat)
}
q"..$loggedStats"
}
}
It produces this error message in Scala 2.11.1:
Q.scala:9: error: macro implementation reference has wrong shape. required:
macro [<static object>].<method name>[[<type args>]] or
macro [<macro bundle>].<method name>[[<type args>]]
def apply[T](x: =>T): T = macro impl
^
I've tried varying the code in numerous ways (import reflect.macros.whitebox.Context, import reflect.macros.blackbox.Context, putting scala. at the start of each import, making the arguments consistently by-name or consistently by-value, macro impl[T], getting rid of the type parameter, macro debug.impl, putting the apply after the impl, and more) with no success. What am I doing wrong? Is it something in the imports? Those come (mostly) from a different web page, http://docs.scala-lang.org/overviews/quasiquotes/setup.html.
The same error occurs on both of the other example macros from that page:
object Macro {
def apply(x: Int): Int = macro impl
def impl(c: Context)(x: c.Expr[Int]): c.Expr[Int] = { import c.universe._
c.Expr(q"$x + 1")
}
}
object Macro {
def apply(x: Int): Int = macro impl
def impl(c: Context)(x: c.Tree) = { import c.universe._
q"$x + 1"
}
}
scala Foo.scala wraps the code that you feed to it in an anonymous class. As a result, something like:
import scala.reflect.macros.Context
import scala.language.experimental.macros
object debug {
def apply[T](x: T): T = macro impl
def impl(c: Context)(x: c.Tree): c.Tree = ???
}
Gets transformed into:
[[syntax trees at end of parser]] // Test.scala
package <empty> {
object Main extends scala.AnyRef {
def <init>() = {
super.<init>();
()
};
def main(args: Array[String]): scala.Unit = {
final class $anon extends scala.AnyRef {
def <init>() = {
super.<init>();
()
};
import scala.reflect.macros.Context;
import scala.language.experimental.macros;
object debug extends scala.AnyRef {
def <init>() = {
super.<init>();
()
};
<macro> def apply[T](x: T): T = impl;
def impl(c: Context)(x: c.Tree): c.Tree = $qmark$qmark$qmark
}
};
new $anon()
}
}
}
However, macro implementations must be defined in static object or bundles, which is what the error messages tries to say:
/Users/xeno_by/Projects/211x/sandbox/Test.scala:5: error: macro implementation reference has wrong shape. required:
macro [<static object>].<method name>[[<type args>]] or
macro [<macro bundle>].<method name>[[<type args>]]
def apply[T](x: T): T = macro impl
^
one error found
Unfortunately, if something is put into inside an anonymous class, it's no longer static, which means that scala Foo.scala-style development is incompatible with macros. Consider using alternatives, e.g. scalac or sbt.