Question

I would like to rewrite partial function into match expression.

//macro and impl
def pfRewrite(pf: PartialFunction[Int, Int]) = macro ResponderImpl.pfRewriteImpl

def pfRewriteImpl(c: Context)(pf: c.Expr[PartialFunction[Int, Int]]) = {
  import c.universe._

  val cas = pf.tree.collect {
    case x: DefDef if x.name.decoded == "applyOrElse" =>
      x.collect {
        case cq"$x => $y" =>  cq"""$x => $y"""
      }
  }.flatten

  val sVal = newTermName("someVal")

  val rewrite = q"""
    $sVal match {
      case ..$cas
    }
  """
  c.Expr(rewrite)
}

In code i got a PartialFunction and take a cases from applyOrElse method, next i create a match expression for "someVal". This value from code:

 //test
 def test {
  val someVal = 10

  pfRewrite {
   case 1 => 10
   case _ => 100
  }
 }

But i got errors:

[error]  found   : Int(10)
[error]  required: Nothing
[error]       case 1 => 10
[error]                 ^

and etc.

It possible rewrite partial function call into match ?

Was it helpful?

Solution

Typically these kinds of awkward error messages stem from mixing typed and untyped trees (for more details take a look at Scala macros: What is the difference between typed (aka typechecked) an untyped Trees). Therefore, until we've fixed the situation, it's good practice to do resetLocalAttrs on typed trees that you plan to mix with untyped ones.

This case is not an exception. Using c.resetLocalAttrs(pf.tree) instead of just pf.tree reveals the real problem with the macro expansion:

12:57 ~/Projects/Master/sandbox (master)$ scalac Test.scala
Test.scala:5: error: not found: value default
     Macros.pfRewrite {
                      ^

If we enable -Xprint:typer, which is one of the ways to see macro expansions (along with -Ymacro-debug-lite and -Ymacro-debug-verbose), then we'll see what's going on:

[[syntax trees at end of                     typer]] // Test.scala
package <empty> {
  object Test extends AnyRef with App {
    def <init>(): Test.type = {
      Test.super.<init>();
      ()
    };
    def test: Unit = {
      val someVal: Int = 10;
      {
        someVal match {
          case 1 => 10
          case _ => 100
          case (defaultCase$ @ _) => <default: error>.apply(<x1: error>)
        };
        ()
      }
    }
  }
}

It looks like partial function synthesis also adds a default case that we can't reuse as-is, as it refers to some synthetic variables defined in the synthesized class.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top