みなさんScalaのマクロ使っていますか?

  • あまり使ってないですよね…?
  • Scalaのライブラリを作るには有用な技術です
  • 今日は5分でScalaのマクロを理解していただきます

そもそもマクロとは?

  • 構文木を変更したり、生成するもの
  • コンパイル時に計算・処理をする

マクロの用途

  • コードの生成
  • 静的(コンパイル時)チェックの強化
  • 新しい構文の導入(DSLのためなど)

Scalaのマクロの種類

  • defマクロ(関数マクロ)
  • マクロアノテーション New!

defマクロ

def assert(cond: Boolean, msg: Any) = macro AssertMacro.impl
  • Scala 2.10の標準で使える
  • マクロの利用者は普通の関数のように使える
  • 型つき

defマクロの特徴

  • 型つきなのでマクロの呼び出しが型チェックされる
  • 引数の型情報が取れる
  • implicit defもできるよ(2.10.2から)
  • 関数なのでScalaの値しか返すことができない
  • ということはクラスや型の定義はできない!

マクロアノテーション

class Model extends StaticAnnotation {
  def macroTransform(annottees: Any*) = macro ModelsMacro.impl
}
@Model object Models {
   case class Company(name: String, website: String)
   case class Member(name: String, age: Int)

   // このへんにテーブル定義を生成してくれるようなマクロを作れる
}

マクロアノテーション

  • アノテーションを付けたものを変換する
  • 型なし
  • マクロパラダイスプラグインを使わないといけない

マクロアノテーションの特徴

  • 色々なところにアノテーションを付けることができる
  • クラスや型を引数にしたり生成したりできる
  • コンパニオン・オブジェクトをいじれる

で、マクロの本体ってどうやって書くのさ?

def impl(c: Context)(cond: c.Expr[Boolean]) = {
  //                  ↑ 引数が構文木に変換されている
  import c.universe._
  // ここまで定型文みたいなもん

  // 引数の構文木解析やコンパイル時計算

  // 返す構文木を書く
}

構文木の書き方

  • 構文木のケースクラスを組み立てる
  • reifyを使う
  • 準クォートを使う

ケースクラスで書く

Apply(
  Ident(newTermName("println")),
  List(Literal(Constant("Hello World"))))
  • 自由に詳細に書ける
  • 量が多くなり、書きづらい、読みづらい

reifyを使う

reify(println("Hello " + name.splice))
  • reify自体もdefマクロ
  • Scalaの式なので書きやすく読みやすい
  • Scalaの式として正しいものでないと引数に取れない

準クォートを使う

q"""
class ${newTypeName(name)} {
  def ${newTermName(method)} = "hello"
}"""
  • 構文木を引数にとるString Interpolation
  • reifyでは引数にできなかったところを引数にできる
  • マクロパラダイスプラグインに入っている

これでScalaのマクロが書ける!

<Thank You!>