2013-08-26 6 views
6

部分的な機能を持つScalaマクロを実装し、その関数のパターンをいくつか変換して、与えられた式に適用します。私は、一部の機能を分解applyOrElse機能の症例定義を選択し、それぞれの定義に必要な変換を実行し、戻って一緒にすべてをかけるScalaマクロを使用して部分関数を変換して適用する方法は?

def myMatchImpl[A: c.WeakTypeTag, B: c.WeakTypeTag](c: Context)(expr: c.Expr[A])(patterns: c.Expr[PartialFunction[A, B]]): c.Expr[B] = { 
    import c.universe._ 

    /* 
    * Deconstruct the partial function and select the relevant case definitions. 
    * 
    * A partial, anonymus function will be translated into a new class of the following form: 
    * 
    * { @SerialVersionUID(0) final <synthetic> class $anonfun extends scala.runtime.AbstractPartialFunction[A,B] with Serializable { 
    * 
    *  def <init>(): anonymous class $anonfun = ... 
    * 
    *  final override def applyOrElse[...](x1: ..., default: ...): ... = ... match { 
    *  case ... => ... 
    *  case (defaultCase$ @ _) => default.apply(x1) 
    *  } 
    * 
    *  def isDefined ... 
    * } 
    * new $anonfun() 
    * }: PartialFunction[A,B] 
    * 
    */ 
    val Typed(Block(List(ClassDef(a, b, x, Template(d, e, List(f, DefDef(g, h, i, j, k, Match(l, allCaseDefs)), m)))), n), o) = patterns.tree 

    /* Perform transformation on all cases */ 
    val transformedCaseDefs: List[CaseDef] = allCaseDefs map { 
    case caseDef => caseDef // This code will perform the desired transformations, now it's just identity 
    } 

    /* Construct anonymus partial function with transformed case patterns */ 
    val result = Typed(Block(List(ClassDef(a, b, x, Template(d, e, List(f, DefDef(g, h, i, j, k, Match(l, transformedCaseDefs)), m)))), n), o) 
    // println(show(result)) 

    c.Expr[B](q"$result($expr)") 
} 

:そうするには

は、私は次のコードで開始しました。マクロは次のように呼び出されます。

def myMatch[A, B](expr: A)(patterns: PartialFunction[A, B]): B = macro myMatchImpl[A,B] 

残念ながら、これは期待どおりに動作しません。

object creation impossible, since method isDefinedAt in trait PartialFunction of type (x: Option[Int])Boolean is not defined 

生成された部分関数収率をどの明確

({ 
    final <synthetic> class $anonfun extends scala.runtime.AbstractPartialFunction[Option[Int],Int] with Serializable { 
    def <init>(): anonymous class $anonfun = { 
     $anonfun.super.<init>(); 
    () 
    }; 
    final override def applyOrElse[A1 <: Option[Int], B1 >: Int](x2: A1, default: A1 => B1): B1 = ((x2.asInstanceOf[Option[Int]]: Option[Int]): Option[Int] @unchecked) match { 
     case (x: Int)Some[Int]((n @ _)) => n 
     case scala.None => 0 
    }; 
    final def isDefinedAt(x2: Option[Int]): Boolean = ((x2.asInstanceOf[Option[Int]]: Option[Int]): Option[Int] @unchecked) match { 
     case (x: Int)Some[Int]((n @ _)) => true 
     case scala.None => true 
     case (defaultCase$ @ _) => false 
    } 
    }; 
    new $anonfun() 
}: PartialFunction[Option[Int],Int]) 

を印刷するので、これは、少し混乱して次のエラーメッセージで

def test(x: Option[Int]) = myMatch(x){ 
    case Some(n) => n 
    case None => 0 
} 

結果は単純な例ではマクロを使用してisDefinedAtメソッドを定義します。

誰もがアイデアを持っていますが、何が問題なのですか、これをどうやって行うのですか?

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

def myMatchImpl[A: c.WeakTypeTag, B: c.WeakTypeTag](c: Context)(
    expr: c.Expr[A] 
)(
    patterns: c.Expr[PartialFunction[A, B]] 
): c.Expr[B] = { 
    import c.universe._ 

    val transformer = new Transformer { 
    override def transformCaseDefs(trees: List[CaseDef]) = trees.map { 
     case caseDef => caseDef 
    } 
    } 

    c.Expr[B](q"${transformer.transform(patterns.tree)}($expr)") 
} 

def myMatch[A, B](expr: A)(patterns: PartialFunction[A, B]): B = 
    macro myMatchImpl[A,B] 

def test(x: Option[Int]) = myMatch(x) { 
    case Some(n) => n 
    case None => 0 
} 

あなたは変換が唯一の場合に適用されていることを確認するために、いくつかの追加の機械が必要になる場合があります

+1

質問の中で最も興味深い部分は答えではありませんが、['Transformer'](http://www.scala-lang.org/api/current/index.html#scala.reflect .api.Trees $ Transformer)?それはおそらくより頑強に(あなたが望むように)(https://gist.github.com/travisbrown/6340753)するべきです。 –

+0

@TravisBrown Transformerクラスはまさに私が探していたものです。ヒントありがとうございます。あなたの要点を答えとして投稿してください。 –

答えて

4

新しいリフレクションAPIは、具体的には、ツリー変換のこの種のを支援するために設計されていますTransformerクラスを提供しますあなたがそれを適用したいリストですが、一般に、このアプローチはツリーを手動で変換するよりも堅牢になるでしょう。

あなたのバージョンがなぜ機能しないのか不思議ですが、時間があれば、別の質問のための縮小例をまとめておく価値があります。

+0

マクロ展開で型付き木と型なし木を混在させることのいくつかの厄介な副作用があると思います。部分的な関数合成は、部分的に型付けされた入力を覚えてしまうかもしれない魔法のかなりの部分を呼び出す。 –

関連する問題