[解決済み] マクロから匿名クラスのメソッドを持つ構造型を取得する
質問内容
ある型のメンバやメソッドを持つ無名クラスを定義し、それらのメソッドなどを持つ構造型として静的に型付けされたそのクラスのインスタンスを作成するマクロを書きたいとします。これは 2.10.0 のマクロ システムで可能であり、タイプ メンバの部分は非常に簡単です。
object MacroExample extends ReflectionUtils {
import scala.language.experimental.macros
import scala.reflect.macros.Context
def foo(name: String): Any = macro foo_impl
def foo_impl(c: Context)(name: c.Expr[String]) = {
import c.universe._
val Literal(Constant(lit: String)) = name.tree
val anon = newTypeName(c.fresh)
c.Expr(Block(
ClassDef(
Modifiers(Flag.FINAL), anon, Nil, Template(
Nil, emptyValDef, List(
constructor(c.universe),
TypeDef(Modifiers(), newTypeName(lit), Nil, TypeTree(typeOf[Int]))
)
)
),
Apply(Select(New(Ident(anon)), nme.CONSTRUCTOR), Nil)
))
}
}
(ここで
ReflectionUtils
は
便利な特性
を提供し、私の
constructor
というメソッドがあります)。
このマクロは、匿名クラスの型メンバの名前を文字列リテラルで指定します。
scala> MacroExample.foo("T")
res0: AnyRef{type T = Int} = $1$$1@7da533f6
適切に型付けされていることに注意してください。すべてが期待通りに動いていることが確認できます。
scala> implicitly[res0.T =:= Int]
res1: =:=[res0.T,Int] = <function1>
では、同じことをメソッドでやってみるとします。
def bar(name: String): Any = macro bar_impl
def bar_impl(c: Context)(name: c.Expr[String]) = {
import c.universe._
val Literal(Constant(lit: String)) = name.tree
val anon = newTypeName(c.fresh)
c.Expr(Block(
ClassDef(
Modifiers(Flag.FINAL), anon, Nil, Template(
Nil, emptyValDef, List(
constructor(c.universe),
DefDef(
Modifiers(), newTermName(lit), Nil, Nil, TypeTree(),
c.literal(42).tree
)
)
)
),
Apply(Select(New(Ident(anon)), nme.CONSTRUCTOR), Nil)
))
}
しかし、実際に試してみると、構造型は出てきません。
scala> MacroExample.bar("test")
res1: AnyRef = $1$$1@da12492
しかし、そこに余分な匿名クラスを貼り付けると
def baz(name: String): Any = macro baz_impl
def baz_impl(c: Context)(name: c.Expr[String]) = {
import c.universe._
val Literal(Constant(lit: String)) = name.tree
val anon = newTypeName(c.fresh)
val wrapper = newTypeName(c.fresh)
c.Expr(Block(
ClassDef(
Modifiers(), anon, Nil, Template(
Nil, emptyValDef, List(
constructor(c.universe),
DefDef(
Modifiers(), newTermName(lit), Nil, Nil, TypeTree(),
c.literal(42).tree
)
)
)
),
ClassDef(
Modifiers(Flag.FINAL), wrapper, Nil,
Template(Ident(anon) :: Nil, emptyValDef, constructor(c.universe) :: Nil)
),
Apply(Select(New(Ident(wrapper)), nme.CONSTRUCTOR), Nil)
))
}
動作します。
scala> MacroExample.baz("test")
res0: AnyRef{def test: Int} = $2$$1@6663f834
scala> res0.test
res1: Int = 42
これは非常に便利で、次のようなことができます。
これ
しかし、なぜこれが機能し、タイプメンバー版が機能し、そして
bar
. 私はこれを知っている
は定義されていないかもしれません。
しかし、それは何か意味があるのでしょうか?マクロから構造型(メソッド付き)を取得するもっと簡単な方法はありますか?
どのように解決するのですか?
この質問は、Travisが重複して回答しています。 こちら . トラッカーにある問題へのリンクと、Eugeneの議論(コメントとメーリングリスト)へのリンクがあります。
タイプチェッカーの有名なセクション「Skylla and Charybdis"」では、主人公が暗い匿名性を脱し、構造型のメンバーとして光を見るべきものを決定します。
タイプチェッカーを騙す方法はいくつかある(オデュッセウスが羊を抱きしめるような策略は必要ない)。 一番簡単なのは、ダミーのステートメントを挿入して、ブロックが無名クラスとそのインスタンス化のように見えないようにすることです。
タイパーが、外部から参照されないパブリックな項であることに気づいたら、プライベートにしてくれる。
object Mac {
import scala.language.experimental.macros
import scala.reflect.macros.Context
/* Make an instance of a structural type with the named member. */
def bar(name: String): Any = macro bar_impl
def bar_impl(c: Context)(name: c.Expr[String]) = {
import c.universe._
val anon = TypeName(c.freshName)
// next week, val q"${s: String}" = name.tree
val Literal(Constant(s: String)) = name.tree
val A = TermName(s)
val dmmy = TermName(c.freshName)
val tree = q"""
class $anon {
def $A(i: Int): Int = 2 * i
}
val $dmmy = 0
new $anon
"""
// other ploys
//(new $anon).asInstanceOf[{ def $A(i: Int): Int }]
// reference the member
//val res = new $anon
//val $dmmy = res.$A _
//res
// the canonical ploy
//new $anon { } // braces required
c.Expr(tree)
}
}
関連
-
[解決済み] Scalaでswitch/case(単純なパターンマッチ)を使うには?
-
[解決済み] Scalaでループから抜け出すにはどうしたらいいですか?
-
[解決済み] Scala subString関数
-
[解決済み] MapのmapValuesとtransformの違いについて
-
[解決済み] Scalaのapply関数とは何ですか?
-
[解決済み】Scalaのvarとvalの定義の違いは何ですか?
-
[解決済み】ScalaのCaseオブジェクトとEnumerationsの比較
-
[解決済み】ScalaのJavaConvertersとJavaConversionsの違いは何ですか?
-
[解決済み] 縮小、折りたたみ、スキャン(左/右)?
-
[解決済み] 型の論理和(ユニオン型)はどのように定義するのですか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] Scalaでステートメントの間にN秒待つには?
-
[解決済み] GSON JsonObject "Unsupported Operation Exception: null" getAsString
-
[解決済み] SparkSQL - パーケットファイルを直接読み込む
-
[解決済み] ScalaのバージョンをScala本体から取得するにはどうしたらいいですか?
-
[解決済み] Spark - CSVファイルをDataFrameとして読み込む?
-
[解決済み】Scala 2.8のコレクション・ライブラリは「歴史上最も長い遺書」のケースか?[クローズド] Scala
-
[解決済み】ScalaのHigher kinded typeとは何ですか?
-
[解決済み】コマンドラインパラメータを解析する最良の方法?[クローズド]
-
[解決済み】タスクがシリアライズされない:オブジェクトではなくクラスに対してのみクロージャの外で関数を呼び出すとjava.io.NotSerializableExceptionが発生する
-
[解決済み] Scala:1ステートメントで文字列をファイルに書き込む