Scala macros assign parameter to deconstructed function

I'm playing a little with macros right now and maybe this is a bad idea, but here is my problem:

I have the following macro:

def using[A <: { def close(): Unit }, B](resource: A)(f: A => B) = macro usingImpl[A, B] def usingImpl[A <: { def close(): Unit }, B](c: Context)(resource: c.Expr[A])(f: c.Expr[A => B]): c.Expr[B] = { import c.universe._ f.tree match { case Function(params, body) => //val ValDef(modifiers, name, tpt, _) = params.head c.Expr[B]( Block( List( //ValDef(modifiers, name, tpt, resource.tree) ValDef(params.head.symbol, resource.tree) ), body ) ) case _: Select => reify { val res = resource.splice try { f.splice(res) } finally { res.close() } } } } 

In the case of Select I just call the function and close the resource, it works fine. But in the case of Function I would like to assign a parameter value to a resource and call the body. When I use the deprecated ValDef creator that accepts Symbol and Tree , everything works fine. If I use the creator with 4-args comments, I get a compiler error stating that the value x$1 not included in the volume. When I look at the code that both versions produce, it looks exactly the same:

 Expr[Int]({ <synthetic> val x$1: Test.Foo = new Test.this.Foo(); x$1.bar.+(23) }) 

Is there a way to just use params.head and assign a value? Thanks for any help!

change

I call the macro as follows:

 object Test extends App { import Macros._ class Foo { def close() {} def bar = 3 } println(using(new Foo)(_.bar + 3)) } 

As I said, if I use an outdated version, it gives me a compiler error that prints the AST and at the end of this message: [error] symbol value x$1 does not exist in Test$delayedInit$body.apply

And I use 2.10.1.

+6
source share
1 answer

Ah, now I can reproduce your mistake. Every time you get "This entry seems to have killed the compiler." and see lines like this in the stack trace:

 at scala.reflect.internal.SymbolTable.abort(SymbolTable.scala:49) 

The next step should be to start packaging in c.resetAllAttrs . Honestly, I could not reproduce your error for the first time, because I replaced the body in the c.resetAllAttrs(body) block after copying and pasting the code without even thinking about it. This is just a reflex for the moment.

See, for example, this small demonstration of one abstract class method for a similar instance of the place where this trick is needed.

In your case, if we have this:

 import scala.language.experimental.macros import scala.reflect.macros.Context object Macros { def using[A <: { def close(): Unit }, B](resource: A)(f: A => B) = macro usingImpl[A, B] def usingImpl[A <: { def close(): Unit }, B](c: Context) (resource: c.Expr[A])(f: c.Expr[A => B]): c.Expr[B] = { import c.universe._ val expr = f.tree match { case Function(ValDef(modifiers, name, tpt, _) :: Nil, body) => c.Expr[B]( Block( ValDef(modifiers, name, tpt, resource.tree) :: Nil, c.resetAllAttrs(body) ) ) case _: Select => reify { val res = resource.splice try { f.splice(res) } finally { res.close() } } } println(expr) expr } } 

We will see the following when we compile your test code:

 Expr[B]({ <synthetic> val x$1: $line3.$read.$iw.$iw.Test.Foo = new Test.this.Foo(); x$1.bar.$plus(3) }) 

Exactly at will.

+7
source

Source: https://habr.com/ru/post/944580/


All Articles