I'm trying to evaluate an Expr inside a macro using the Context#eval method:
//Dummy implementation
def evalArrayTree(c: Context)(a: c.Expr[ArrayTree]): c.Expr[Array[Double]] = {
import c.universe._
println( c.eval(a) )
val tree = reify( Array(0.0,0.0,0.0) ).tree
c.Expr[Array[Double]]( tree )
}
However, the compiler complains with:
[error] /home/falcone/prg/sbt-example-paradise/core/src/main/scala/Test.scala:20: exception during macro expansion:
[error] scala.tools.reflect.ToolBoxError: reflective toolbox has failed
If found in the scala-user ML, that the problem could be solved using resetAllAttrs. However
I don't understand how I am supposed to use it.
This function seems to be deprecated.
So is there a way to solve my problem ?
The rest of the code:
object ArrayEval {
import scala.language.experimental.macros
def eval( a: ArrayOps.ArrayTree ): Array[Double] = macro Macros.evalArrayTree
}
object ArrayOps {
sealed trait ArrayTree {
def +( that: ArrayTree ) = Plus( this, that )
}
implicit class Ary( val ary: Array[Double] ) extends ArrayTree
case class Plus( left: ArrayTree, right: ArrayTree ) extends ArrayTree
}
The docs for c.eval indeed tell to use c.resetAllAttrs, however this function has a number of known issues that sometimes make it to irreparably corrupt the tree it processes (that's why we're planning to remove it in Scala 2.11 - I just submitted a pull request that does that: https://github.com/scala/scala/pull/3485).
What you could try instead is c.resetLocalAttrs, which has smaller potential for tree corruption. Unfortunately it's still a bit broken. We plan to fix it (https://groups.google.com/forum/#!topic/scala-internals/TtCTPlj_qcQ), however in Scala 2.10.x and 2.11.0 there's going to be no way to make c.eval work reliably.
Well, I figured out what they meant by using resetAllAttrs. My example is simplified for an Int input, but I was able to replicate and fix the error you described by doing the following:
import scala.language.experimental.macros
import scala.reflect.runtime.universe._
import scala.reflect.macros.BlackboxContext
def _evalMacro(c: BlackboxContext)(a: c.Expr[Int]) = {
import c.universe._
val treeReset = c.resetAllAttrs(a.tree) // Reset the symbols in the tree for 'a'
val newExpr = c.Expr(treeReset) // Construct a new expression for the updated tree
println(c.eval(newExpr)) // Perform evaluation on the newly constructed expression
... // Do what you do
}
def evalMacro(a: Int) = macro _evalMacro
I'm going to make a guess that you're fine for using resetAllAttrs, at least until some future versions of Scala come out. 2.11 doesn't even give a deprecation warning for its use.
Note: I'm using Scala 2.11. I believe this should be identical in 2.10, except you'll be using Context instead of BlackboxContext.
Related
We have been banging our heads for a while on this but we cannot find a solution.
In our project we would like to write some DSL to migrate some old code in our codebase.
We would like to make a macro that given an instance of a case class gives us the possibility to extract the value in a typesafe manner. In this case it should be possible to declare x of type Int.
case class MyPersonalCaseClass(token: Int, str: String)
val someVariable = MyPersonalCaseClass(123, "SOMESTRING")
val x = Macros.->(someVariable, "token")
Here “token” is a compile-time constant, referring to the field name.
The macro can be declared with something like
def ->[T](value:T,key: String): Any = macro MacrosImpl.arrow[T]
As for our understanding the only way was with whitebox macros, feel free to change the signatures.
def arrow[T: c.WeakTypeTag](c: whitebox.Context)(value: c.Expr[T], key:c.Expr[String]): c.Expr[Any] =
{
import c.universe._
val caseClassType: c.universe.Type = weakTypeOf[T]
???
}
Scala version is “2.12.8”.
The reason we need something like this is we are porting a lot of code from perl and we would like to give the programmers a vagueish idea they are still writing it.
thanks in advance!
Try
import shapeless.LabelledGeneric
import shapeless.record._
LabelledGeneric[MyPersonalCaseClass].to(someVariable).get(Symbol("token")) // 123
I'm using Scala toolbox to eval some Scala code for a web interpreter. It is working well, the code looks like this:
import scala.reflect.runtime.universe._
import scala.tools.reflect.ToolBox
object Eval {
val toolbox = runtimeMirror(getClass.getClassLoader).mkToolBox()
def eval[T](code: String): T = {
toolbox.eval(toolbox.parse(code)).asInstanceOf[T]
}
}
I can do something like:
Eval.eval[Long]("1 + 1")
and get 2 back. The question comes up when I want to define something:
Eval.eval[Unit]("val yellow = 5")
Eval.eval[Long]("yellow")
I get a not found: value yellow error. How do I define a value that I can use in later evaluations with Scala Toolbox?
For a persistent environment, you can use Scala's REPL directly. See the JSR-223 note at the very bottom of the 2.11 release notes.
import javax.script.ScriptEngineManager
class DummyClass
object Evaluator {
val engine = new ScriptEngineManager().getEngineByName("scala")
val settings = engine.asInstanceOf[scala.tools.nsc.interpreter.IMain].settings
settings.embeddedDefaults[DummyClass]
engine.eval("val x: Int = 5")
val thing = engine.eval("x + 9").asInstanceOf[Int]
}
The need for DummyClass (or really any class you have can substitute for DummyClass) arises from a bit of shenanigans going on here due to SBT and classloader concerns (more details here).
I'm using Scala Pickling, an automatic serialization framework for Scala.
According to the author's slides, any type T can be pickled as long as there is an implicit Pickler[T] in scope.
Here, I'm assuming she means scala.tools.nsc.io.Pickler.
However, the following does not compile:
import scala.pickling._
import scala.pickling.binary._
import scala.tools.nsc.io.Pickler
object Foo {
def bar[T: Pickler](t: T) = t.pickle
}
The error is:
[error] exception during macro expansion:
[error] scala.ScalaReflectionException: type T is not a class
[error] at scala.reflect.api.Symbols$SymbolApi$class.asClass(Symbols.scala:323)
[error] at scala.reflect.internal.Symbols$SymbolContextApiImpl.asClass(Symbols.scala:73)
[error] at scala.pickling.PickleMacros$class.pickleInto(Macros.scala:381)
[error] at scala.pickling.Compat$$anon$17.pickleInto(Compat.scala:33)
[error] at scala.pickling.Compat$.PickleMacros_pickleInto(Compat.scala:34)
I'm using Scala 2.10.2 with scala-pickling 0.8-SNAPSHOT.
Is this a bug or user error?
EDIT 1: The same error arises with both scala.pickling.SPickler and scala.pickling.DPickler.
EDIT 2: It looks like this is a bug: https://github.com/scala/pickling/issues/31
Yep, as Andy pointed out:
you need either a scala.pickling.SPickler or a scala.pickling.DPickler (static and dynamic, respectively) in order to pickle a particular type.
Those both already come in the scala.pickling package, so it's enough to just use them in your generic method signature.
You're absolutely correct that you can add an SPickler context-bound to your generic method. The only additional thing which you need (admittedly it's a bit ugly, and we're thinking about removing it) is to add a FastTypeTag context bound as well. (This is necessary for the pickling framework to know what type it's trying to pickle, as it handles primitives differently, for example.)
This is what you'd need to do to provide generic pickling/unpickling methods:
Note that for the unbar method, you need to provide an Unpickler context-bound rather than a SPickler context-bound.
import scala.pickling._
import binary._
object Foo {
def bar[T: SPickler: FastTypeTag](t: T) = t.pickle
def unbar[T: Unpickler: FastTypeTag](bytes: Array[Byte]) = bytes.unpickle[T]
}
Testing this in the REPL, you get:
scala> Foo.bar(42)
res0: scala.pickling.binary.BinaryPickle =
BinaryPickle([0,0,0,9,115,99,97,108,97,46,73,110,116,0,0,0,42])
scala> Foo.unbar[Int](res0.value)
res1: Int = 42
Looking at the project, it seems you need either an scala.pickling.SPickler or a scala.pickling.DPickler (static and dynamic, respectively) in order to pickle a particular type.
The pickle methods are macros. I suspect that if you pickle with an SPickler, the macro will require the compile time type of your class to be known.
Thus, you may need to do something similar to:
object Foo {
def bar(t: SomeClass1) = t.pickle
def bar(t: SomeClass2) = t.pickle
def bar(t: SomeClass3) = t.pickle
// etc
}
Alternatively, a DPickler may do the trick. I suspect that you'll still have to write some custom pickling logic for your specific types.
I have java API which return this type:
ArrayList[ArrayList[String]] = Foo.someJavaMethod()
In scala program, I need to send above type as a parameter to a scala function 'bar' whose type is
def bar(param: List[List[String]]) : List[String] = {
}
so I call bar like:
val list = bar(Foo.someJavaMethod())
but this does not work as I get compile error.
I thought have this import
import scala.collection.JavaConversions._
will do implicit automatic conversion between Java and Scala collections.
I also tried using like:
Foo.someJavaMethod().toList
but that does not work either.
What is the solution to this problem?
First, ArrayList does not convert to List, it converts to a Scala Buffer. Second, implicit conversion will not recurse into the elements of your collections.
You'll have to manually map the inner lists. Either with implicit conversions:
import collection.JavaConversions._
val f = Foo.someJavaMethod()
bar(f.toList.map(_.toList))
Or, more explicitly, if you prefer:
import collection.JavaConverters._
val f = Foo.someJavaMethod()
bar(f.asScala.toList.map(_.asScala.toList))
Is it possible to get the name of a scala variable at runtime?
E.g. is it possible to write a function getIntVarName(variable: Int): String behaving as follows?
val myInt = 3
assert("myInt" === getIntVarName(myInt))
For what you need to do, It seems to me that runtime is not required, since you already have your myInt variable defined at compile time. If this is the case, you just need a bit of AST manipulation via a macro.
Try
package com.natalinobusa.macros
import scala.language.experimental.macros
import scala.reflect.macros.blackbox.Context
object Macros {
// write macros here
def getName(x: Any): String = macro impl
def impl(c: Context)(x: c.Tree): c.Tree = {
import c.universe._
val p = x match {
case Select(_, TermName(s)) => s
case _ => ""
}
q"$p"
}
}
Be aware that macro's must be compiled as a separate subproject, and cannot be part of the same project where the macro substitution has to be applied. Check this template on how to define such a macro sub-project: https://github.com/echojc/scala-macro-template
scala> import Macros._
import Macros._
scala> val myInt = 3
myInt: Int = 3
scala> "myInt" == getName(myInt)
res6: Boolean = true
You can use scala-nameof to get a variable name, function name, class member name, or type name. It happens at compile-time so there's no reflection involved and no runtime dependency needed.
val myInt = 3
assert("myInt" === nameOf(myInt))
will compile to:
val myInt = 3
assert("myInt" === "myInt")
Basically, it can't be done.
The JVM offers nothing by way of a Method handle (remember, Scala properties are encoded as methods in bytecode to support the uniform access principle). The closest you can get is to use reflection to find a list of methods defined on a particular class - which I appreciate doesn't help with your particular need.
It is possible to implement this as a Scala feature, but it would require a compiler plugin to grab the relevant symbol name from the AST and push it into code as a string literal, so not something I could demonstrate in a short code snippet :)
The other naming problem that often comes up in reflection is method parameters. That one at least I can help with. I have a work-in-progress reflection library here that's based on the compiler-generated scala signature as used by scalap. It's nowhere near being ready for serious use, but it is under active development.
Scala doesn't yet have much more than Java in terms of metadata like this. Keep an eye on the Scala Reflection project, but I doubt that will offer access to local variables anytime soon. In the meantime, consider a bytecode inspector library like ASM. Another big caveat: local variable names are lost during compilation, so you'd need to compile in "debug" mode to preserve them.
I don't think it's possible to get the name of a variable, but you can try it with objects:
object Test1 {
def main(args: Array[String]) {
object MyVar {
def value = 1
}
println(MyVar.getClass)
}
}
This prints: class Test1$MyVar$2$. So you can get 'MyVar' out of it.
This can be achieved with Scala 3 Macros (does it at compile time).
Create a Macro object (this must be in a separate file):
import scala.quoted.{Expr, Quotes}
object NameFromVariable :
def inspectCode(x: Expr[Any])(using Quotes): Expr[String] =
val name = x.show.split("""\.""").last
Expr(name)
Then you need an inline method in your class.
inline def getIntVarName(inline x: Any): Any = ${ NameFromVariable.inspectCode('x) }
And use this method, like:
val myInt = 3
assert("myInt" === getIntVarName(myInt))
See the official documentation: https://docs.scala-lang.org/scala3/guides/macros/macros.html