Макрос Scala: переписать частичную функцию в соответствие

Я хотел бы переписать partial function в выражение match.

//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)
}

В коде я получил PartialFunction и взял метод cases из applyOrElse, затем я создал выражение match для "someVal". Это значение из кода:

 //test
 def test {
  val someVal = 10

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

Но я получил ошибки:

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

и так далее.

Можно ли переписать частичный вызов функции в match ?


person mike    schedule 10.03.2014    source источник
comment
Не могли бы вы указать, какое дерево вывода вы ожидаете?   -  person 0__    schedule 11.03.2014
comment
Он будет использоваться во время выполнения и someVal в качестве параметров метода: def mth(s: Int) = { pfRewrite { case 1 => 10 ; case _ => 100 } } преобразуется в mth(s: Int) = { s match { ... } }. Или что вы имеете в виду?   -  person mike    schedule 11.03.2014
comment
Как pfRewrite узнает, что он должен соответствовать s?   -  person Eugene Burmako    schedule 12.03.2014
comment
Кроме того, вы пытались вызвать resetLocalAttrs при перезаписи?   -  person Eugene Burmako    schedule 12.03.2014
comment
@EugeneBurmako нет, я не использовал resetLocalAttrs. s — это имя атрибута. Теперь я хочу переписать case блоков - case x: Int => x перевести в case x: Int => x + 100. Это возможно или это сложно?   -  person mike    schedule 12.03.2014
comment
По-прежнему не понимаю, как макрос выясняет, что он должен соответствовать s. Не могли бы вы уточнить?   -  person Eugene Burmako    schedule 12.03.2014
comment
Да, должно быть возможно.   -  person Eugene Burmako    schedule 12.03.2014


Ответы (1)


Обычно такие неудобные сообщения об ошибках возникают из-за смешивания типизированных и нетипизированных деревьев (для получения более подробной информации см. -between-typed-aka-typechecked-an-untyped">Макросы Scala: в чем разница между типизированными (также известными как typechecked) и нетипизированными деревьями). Поэтому, пока мы не исправим ситуацию, рекомендуется выполнять resetLocalAttrs для типизированных деревьев, которые вы планируете смешивать с нетипизированными.

Этот случай не исключение. Использование c.resetLocalAttrs(pf.tree) вместо pf.tree показывает реальную проблему с расширением макроса:

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

Если мы включим -Xprint:typer, что является одним из способов просмотра раскрытия макросов (наряду с -Ymacro-debug-lite и -Ymacro-debug-verbose), то мы увидим, что происходит:

[[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>)
        };
        ()
      }
    }
  }
}

Похоже, что частичный синтез функций также добавляет случай по умолчанию, который мы не можем повторно использовать как есть, поскольку он относится к некоторым синтетическим переменным, определенным в синтезируемом классе.

person Eugene Burmako    schedule 12.03.2014