Macro: is there a way to reify a spliced function?

202 views
Skip to first unread message

Léonard Schneider

unread,
Nov 14, 2012, 7:36:15 PM11/14/12
to scala...@googlegroups.com
Hi,

I need to reify an expression of a spliced function. Unfortunately, reify does not work as expected when you append a wildcard. Instead of selecting the function which is being spliced it acts as if the spliced function were a value.

An example to illustrate the issue.


Welcome to Scala version 2.10.0-RC2 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_37).
Type in expressions to have them evaluated.
Type :help for more information.
scala> import scala.reflect.runtime.{universe=>ru}
import scala.reflect.runtime.{universe=>ru}
scala> def f(x: Int) = 0
f: (x: Int)Int
scala> val g = ru.reify(f _)
g: reflect.runtime.universe.Expr[Int => Int] = 
Expr[Int => Int]({
  ((x) => f(x))
})
scala> ru.reify(g.splice _)
res0: reflect.runtime.universe.Expr[() => Int => Int] = 
Expr[() => Int => Int]((() => {
  ((x) => f(x))
}))

I would have expected an Expr[Int => Int]. Is this a bug? Is there a workaround?

Best regards,


Leo

Eugene Burmako

unread,
Nov 15, 2012, 4:38:22 AM11/15/12
to scala-user
reify typechecks its arguments before doing any processing, so it
behaves exactly like normal Scala does:

scala> val g1 = f _
g1: Int => Int = <function1>

scala> val g2 = g1 _
g2: () => Int => Int = <function0>

Why it behaves like that is another question.

On Nov 15, 1:36 am, Léonard Schneider <leonard.schnei...@gmail.com>
wrote:

Dave

unread,
Nov 15, 2012, 6:08:08 AM11/15/12
to scala...@googlegroups.com
Maybe with parentheses to force apply after lifting otherwise it means only lifting.
Don't know if this is intended.


scala> val g2 = g1 _
g2: () => Int => Int = <function0>

scala> val g2 = g1(_)
g2: Int => Int = <function1>


scala> ru.reify(g.splice _)
res1: reflect.runtime.universe.Expr[() => Int => Int] =

Expr[() => Int => Int]((() => {
  ((x) => f(x))
}))

scala> ru.reify(g.splice(_))
res0: reflect.runtime.universe.Expr[Int => Int] =
Expr[Int => Int](((x$1) => {
  ((x) => f(x))
}.apply(x$1)))



Op donderdag 15 november 2012 10:38:29 UTC+1 schreef Eugene Burmako het volgende:

Dave

unread,
Nov 15, 2012, 6:14:01 AM11/15/12
to scala...@googlegroups.com
Or

scala> ru.reify((g.splice _).apply)
res5: reflect.runtime.universe.Expr[Int => Int] =
Expr[Int => Int]((() => {
  ((x) => f(x))
}).apply())


Op donderdag 15 november 2012 12:08:08 UTC+1 schreef Dave het volgende:

Léonard Schneider

unread,
Nov 15, 2012, 6:46:17 AM11/15/12
to scala...@googlegroups.com
Hi Dave,

I've tried that but it failed for my use case. So let me describe the more general problem I'm trying to solve. Any idea or hint is welcome.

// Challenge: write macro getf such as "def g = getf[A.type]" == "def g = A.f _" for any A object
// which contains a f method which can have any type signature
// This is just an example of an A object containing an f method
// object A {
//  def f(x: Int, s: String)
// }


def getf[T] = macro getfImpl[T]

def getfImpl[T: c.WeakTypeTag](c: Context) = {
  import c.universe._

  ???

}

Léonard Schneider

unread,
Nov 15, 2012, 12:53:36 PM11/15/12
to scala...@googlegroups.com
So here is a failed attempt to fix ideas. It shows why I've been trying to use the splice and wildcard combination in a reify. I think my initial question was a bad example to that regard.

scala> :pas
// Entering paste mode (ctrl-D to finish)
object A {
 def f(x: Int, s: String) = 0 


def getf[T] = macro getfImpl[T]
def getfImpl[T: c.WeakTypeTag](c: Context) = {
  import c.universe._
  c.Expr(treeBuild.mkAttributedIdent(weakTypeOf[T].member(newTermName("f"))))
}
scala> def g = getf[A.type]
<console>:12: error: missing arguments for method f in object A;
follow this method with `_' if you want to treat it as a partially applied function
       def g = getf[A.type]

Léonard Schneider

unread,
Nov 16, 2012, 6:51:31 PM11/16/12
to scala...@googlegroups.com
OK. Answering myself. Here is what I came up with. It would still need to be made more robust by dealing with nullary methods for instance. In a few words, you have to redefine a function which takes the method values, as a lambda expression would, to be able to use it later on. I should be able to use the trick for automatically transform case classes into HList in my implementation ;)

object A {
 def f(x: Int, s: String) = 0 

def getf[T]: Any = macro getfImpl[T]
def getfImpl[T: c.WeakTypeTag](c: Context) = {
  import c.universe._
  val fSym = weakTypeOf[T].member(newTermName("f")).asMethod 
  val argSyms = fSym.paramss.flatten
  val funTree = Function(argSyms.map(ValDef(_)), treeBuild.mkMethodCall(fSym, argSyms.map(s =>
    treeBuild.mkAttributedIdent(s)
  )))
  c.Expr(funTree)

Dave

unread,
Nov 17, 2012, 6:38:53 AM11/17/12
to scala...@googlegroups.com
Amazing. Well done!


C:\Users\Dave>scala
Welcome to Scala version 2.10.0-RC2 (Oracle JRockit(R), Java 1.6.0_37).

Type in expressions to have them evaluated.
Type :help for more information.

scala> import language.experimental.macros
import language.experimental.macros

scala> import scala.reflect.macros.Context
import scala.reflect.macros.Context

scala> :paste

// Entering paste mode (ctrl-D to finish)

object A {
 def f(x: Int, s: String) = 0
}

def getf[T]: Any = macro getfImpl[T]
def getfImpl[T: c.WeakTypeTag](c: Context) = {
  import c.universe._
  val fSym = weakTypeOf[T].member(newTermName("f")).asMethod
  val argSyms = fSym.paramss.flatten
  val funTree = Function(argSyms.map(ValDef(_)), treeBuild.mkMethodCall(fSym, argSyms.map(s =>
    treeBuild.mkAttributedIdent(s)
  )))
  c.Expr(funTree)
}

// Exiting paste mode, now interpreting.

defined module A
getf: [T]=> Any
getfImpl: [T](c: scala.reflect.macros.Context)(implicit evidence$1: c.WeakTypeTa
g[T])c.Expr[T]


scala> def g = getf[A.type]
g: (Int, String) => Int





Op zaterdag 17 november 2012 00:51:31 UTC+1 schreef Léonard Schneider het volgende:
Reply all
Reply to author
Forward
0 new messages