29

This question is similar in motivation to my previous question (although it's about a problem I ran into in a different context).

I can pattern match on a function literal pretty easily without quasiquotes:

import scala.reflect.macros.Context
import scala.language.experimental.macros

object QQExample {
  def funcDemo(f: Int => String) = macro funcDemo_impl
  def funcDemo_impl(c: Context)(f: c.Expr[Int => String]) = {
    import c.universe._

    f.tree match {
      case Function(ps, body) => List(ps, body) foreach println
      case _ => c.abort(
        c.enclosingPosition,
        "Must provide a function literal."
      )
    }

    c.literalUnit
  }
}

Which works like this:

scala> QQExample.funcDemo((a: Int) => a.toString)
List(val a: Int = _)
a.toString()

Now suppose I want to use quasiquotes to do the same kind of match more flexibly. The following will also match on that function, and prints what we'd expect.

case q"($x: $t) => $body" => List(x, t, body) foreach println

But if I want to specify the type in the pattern, it doesn't match:

case q"($x: Int) => $body" => List(x, body) foreach println

And none of the following even compile:

case q"$p => $body"      => List(p,  body) foreach println
case q"($p) => $body"    => List(p,  body) foreach println
case q"..$ps => $body"   => List(ps, body) foreach println
case q"(..$ps) => $body" => List(ps, body) foreach println

Is it possible to specify the type of a parameter when matching on a function literal with quasiquotes, or to match on an unknown number of parameters?

Community
  • 1
  • 1
Travis Brown
  • 138,631
  • 12
  • 375
  • 680
  • 9
    That's a bug. All of the patterns in your last code block should work for single-argument anonymous functions. Follow [SI-7803](https://issues.scala-lang.org/browse/SI-7803) to get notified when the fix is merged into master – Denys Shabalin Sep 02 '13 at 12:06
  • 5
    The fix is sitting in the 2.11 pull requests queue. After it's merged I'll backport it to 2.10. I was also a bit wrong with saying that all patterns will work. You'll have to use parentheses over arguments (i.e. patterns 2 and 4) – Denys Shabalin Sep 05 '13 at 19:23
  • @DenShabalin Should matching against function types work as well? I successfully matched as follows `val q"Capture.capture[$tp]" = tree` with tree as `capture[() => R]`, `capture[A => R]`, `capture[(A, B) => R]`, etc. Next I am failed to match `val tq"(..$ts) => $tr" = tp`. Please, give a hint on how it should be done. – Alexander Myltsev Sep 27 '13 at 16:33
  • 3
    @DenShabalin: shouldn't that be an answer? – SamB Jan 20 '14 at 05:50
  • Matching against function literals and functions types should work in 2.11.x but might not work via paradise plugin for 2.10 atm. There is a plan to backport latest quasiquotes to paradise plugin around 2.11 release. – Denys Shabalin Jan 29 '14 at 14:13
  • @DenShabalin because if it's an answer, then this won't top the unanswered questions list for scala. It doesn't have to be a pleasant answer, or even the correct answer. – som-snytt Feb 01 '14 at 00:15

1 Answers1

8

With latest paradise plugin for 2.10 and in vanilla 2.11 you can do it this way:

val q"(..$args) => $body" = f.tree

I've just tested it out with paradise example project with following Macros.scala:

import language.experimental.macros
import scala.reflect.macros.Context

object Macro {
  def apply(f: Any): Any = macro impl
  def impl(c: Context)(f: c.Expr[Any]) = { import c.universe._
    val q"(..$args) => $body" = f.tree
    println(s"args = $args, body = $body")
    c.Expr(q"()")
  }
}

And Test.scala:

object Test extends App {
  Macro((x: Int) => x + 1)
}

You can read in more about handling of function trees with quasiquotes in corresponding chapter of quasiquote guide.

steinybot
  • 5,491
  • 6
  • 37
  • 55
Denys Shabalin
  • 1,696
  • 12
  • 12