Upgrade to Pro — share decks privately, control downloads, hide ads and more …

meta programming Scala

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
Avatar for petitviolet petitviolet
September 09, 2017

meta programming Scala

Metaprogramming Scala using scala.meta.
performed presentation as Scala-Kansai Summit 2017.

Avatar for petitviolet

petitviolet

September 09, 2017
Tweet

More Decks by petitviolet

Other Decks in Programming

Transcript

  1. 自己紹介 • 小紫 弘貴(Hiroki Komurasaki) • インターネットだと@petitviolet • Scala歴2年くらい •

    でScalaとかGo書いてます • https://www.facebook.com/fringeneer/ • http://fringeneer.hatenablog.com/ 
  2. 今日話すこと • メタプログラミングってなに? • Scalaで出来るメタプログラミング • scala.meta • 使い方イメージ •

    ユースケース・実装例 • 実装方法 • scalametaの内部的な話 • ロードマップ 
  3. 

  4. よく使われるリフレクションもメタプログラミ ングの一種。 実行時/コンパイル時のオブジェクトにアクセス 出来る。  try { Class<?> cl =

    Class.forName("Foo"); Method method = cl.getMethod("hello"); method.invoke(cl.newInstance()); } catch (Exception e) { e.printStackTrace(); }
  5. よく使われるリフレクションもメタプログラミ ングの一種。 実行時/コンパイル時のオブジェクトにアクセス 出来る。  import scala.reflect.runtime.{universe => ru} def

    getTypeTag[T: ru.TypeTag](obj: T) = ru.typeTag[T] val l = List(1, 2, 3) val head: ru.Symbol = getTypeTag(l).tpe.decl(ru.TermName("head")) val lMirror: ru.InstanceMirror = ru.runtimeMirror(getClass.getClassLoader).reflect(l) lMirror.reflectMethod(head.asMethod).apply() // 1
  6. よく使われるリフレクションもメタプログラミ ングの一種。 実行時/コンパイル時のオブジェクトにアクセス 出来る。  import scala.reflect.runtime.{universe => ru} def

    getTypeTag[T: ru.TypeTag](obj: T) = ru.typeTag[T] val l = List(1, 2, 3) val head: ru.Symbol = getTypeTag(l).tpe.decl(ru.TermName("head")) val lMirror: ru.InstanceMirror = ru.runtimeMirror(getClass.getClassLoader).reflect(l) lMirror.reflectMethod(head.asMethod).apply() // 1
  7. 

  8. • def macro • 引数等のメタ情報を利用できる • macro annotation • annotationをつけたクラスやメソッドのメタ情報、例

    えば型情報が利用できる • implicit macro • implicit parameterの自動生成 • type provider • 型そのものを自動生成 • 構造的部分型 • 他にもいろいろ • マクロバンドルとか抽出子マクロとか  http://docs.scala-lang.org/ja/overviews/macros/usecases.html
  9. • def macro • 引数等のメタ情報を利用できる • macro annotation • annotationをつけたクラスやメソッドのメタ情報、例

    えば型情報が利用できる • implicit macro • implicit parameterの自動生成 • type provider • 型そのものを自動生成 • 構造的部分型 • 他にもいろいろ • マクロバンドルとか抽出子マクロとか  http://docs.scala-lang.org/ja/overviews/macros/usecases.html
  10. def macro • Scalaマクロの代表格(要出典  object DefMacro { def show[A](value:

    A): String = macro DefMacroImpl.showImpl[A] } object DefMacroImpl { def showImpl[A: c.WeakTypeTag](c: blackbox.Context) (value: c.Expr[A]): c.Expr[String] = { import c.universe._ val typeName = c.weakTypeTag[A].tpe.typeSymbol.fullName val tree = value.tree c.Expr[String](Literal(Constant(s"type: $typeName, value: $tree"))) } }
  11. def macro • Scalaマクロの代表格(要出典  object DefMacro { def show[A](value:

    A): String = macro DefMacroImpl.showImpl[A] } object DefMacroImpl { def showImpl[A: c.WeakTypeTag](c: blackbox.Context) (value: c.Expr[A]): c.Expr[String] = { import c.universe._ val typeName = c.weakTypeTag[A].tpe.typeSymbol.fullName val tree = value.tree c.Expr[String](Literal(Constant(s"type: $typeName, value: $tree"))) } }
  12. def macro • Scalaマクロの代表格(要出典  object DefMacro { def show[A](value:

    A): String = macro DefMacroImpl.showImpl[A] } object DefMacroImpl { def showImpl[A: c.WeakTypeTag](c: blackbox.Context) (value: c.Expr[A]): c.Expr[String] = { import c.universe._ val typeName = c.weakTypeTag[A].tpe.typeSymbol.fullName val tree = value.tree c.Expr[String](Literal(Constant(s"type: $typeName, value: $tree"))) } }
  13.  object DefMacro { def show[A](value: A): String = macro

    DefMacroImpl.showImpl[A] } object DefMacroImpl { def showImpl[A: c.WeakTypeTag](c: blackbox.Context) (value: c.Expr[A]): c.Expr[String] = { import c.universe._ val typeName = c.weakTypeTag[A].tpe.typeSymbol.fullName val tree = value.tree c.Expr[String](Literal(Constant(s"type: $typeName, value: $tree"))) } } println(DefMacro.show[Int](100)) // type: scala.Int, value: 100 println(DefMacro.show[Long](100L)) // type: scala.Long, value: 100L println(DefMacro.show(new java.lang.String("hello"))) // type: java.lang.String, value: new java.lang.String("hello")
  14.  def log(foo: String)(implicit line: sourcecode.Line, file: sourcecode.File) = {

    println(s"${file.value}:${line.value} $foo") } log("Foooooo") // sourcecode/shared/src/test/scala/sourcecode/Tests.scala:86 Foooooo lihaoyi/sourcecode • ソースコードの行番号やファイル名を取得 • プログラムをメタに扱っている • 内部実装はdef macroを使用
  15. 

  16. 

  17. scalameta? > Scalameta is a clean-room implementation of a metaprogramming

    toolkit for Scala, designed to be simple, robust and portable. > We are striving for scalameta to become a successor of scala.reflect, the current de facto standard in the Scala ecosystem. http://scalameta.org/ 
  18. scalameta? > Scalameta is a clean-room implementation of a metaprogramming

    toolkit for Scala, designed to be simple, robust and portable. > We are striving for scalameta to become a successor of scala.reflect, the current de facto standard in the Scala ecosystem. http://scalameta.org/ 
  19. scalameta? > Scalameta is a clean-room implementation of a metaprogramming

    toolkit for Scala, designed to be simple, robust and portable. > We are striving for scalameta to become a successor of scala.reflect, the current de facto standard in the Scala ecosystem. http://scalameta.org/ 
  20. scalameta? > Scalameta is a clean-room implementation of a metaprogramming

    toolkit for Scala, designed to be simple, robust and portable. > We are striving for scalameta to become a successor of scala.reflect, the current de facto standard in the Scala ecosystem. http://scalameta.org/ 
  21. scalameta? > Scalameta is a modern metaprogramming library for Scala

    that supports a wide range of language versions and execution platforms. > Originally, Scalameta was founded to become a better macro system for Scala, but over time we shifted focus to developer tools and spun off the new macro system into a separate project. http://scalameta.org/ 
  22. 

  23. 

  24. 

  25. 

  26. 

  27. 

  28.  import scala.annotation.{StaticAnnotation, compileTimeOnly} import scala.meta._ @compileTimeOnly("hello") class hello extends

    StaticAnnotation { inline def apply(defn: Any): Any = meta { defn match { case d: Defn.Def => val newBody = q"""{ println("hello") ${d.body} }""" d.copy(body = newBody) case _ => abort("annotate only def") } } }
  29.  import scala.annotation.{StaticAnnotation, compileTimeOnly} import scala.meta._ @compileTimeOnly("hello") class hello extends

    StaticAnnotation { inline def apply(defn: Any): Any = meta { defn match { case d: Defn.Def => val newBody = q"""{ println("hello") ${d.body} }""" d.copy(body = newBody) case _ => abort("annotate only def") } } } scala.metaͷimport
  30.  import scala.annotation.{StaticAnnotation, compileTimeOnly} import scala.meta._ @compileTimeOnly("hello") class hello extends

    StaticAnnotation { inline def apply(defn: Any): Any = meta { defn match { case d: Defn.Def => val newBody = q"""{ println("hello") ${d.body} }""" d.copy(body = newBody) case _ => abort("annotate only def") } } } annotation༻ͷclassͰ͢Αએݴ
  31.  import scala.annotation.{StaticAnnotation, compileTimeOnly} import scala.meta._ @compileTimeOnly("hello") class hello extends

    StaticAnnotation { inline def apply(defn: Any): Any = meta { defn match { case d: Defn.Def => val newBody = q"""{ println("hello") ${d.body} }""" d.copy(body = newBody) case _ => abort("annotate only def") } } } ࣮ߦ͞ΕΔapplyؔ਺
  32.  import scala.annotation.{StaticAnnotation, compileTimeOnly} import scala.meta._ @compileTimeOnly("hello") class hello extends

    StaticAnnotation { inline def apply(defn: Any): Any = meta { defn match { case d: Defn.Def => val newBody = q"""{ println("hello") ${d.body} }""" d.copy(body = newBody) case _ => abort("annotate only def") } } } annotationͨ͠ର৅
  33.  import scala.annotation.{StaticAnnotation, compileTimeOnly} import scala.meta._ @compileTimeOnly("hello") class hello extends

    StaticAnnotation { inline def apply(defn: Any): Any = meta { defn match { case d: Defn.Def => val newBody = q"""{ println("hello") ${d.body} }""" d.copy(body = newBody) case _ => abort("annotate only def") } } } ASTͷॻ͖׵͑
  34.  import scala.annotation.{StaticAnnotation, compileTimeOnly} import scala.meta._ @compileTimeOnly("hello") class hello extends

    StaticAnnotation { inline def apply(defn: Any): Any = meta { defn match { case d: Defn.Def => val newBody = q"""{ println("hello") ${d.body} }""" d.copy(body = newBody) case _ => abort("annotate only def") } } } ৽͍͠AST
  35.  import scala.annotation.{StaticAnnotation, compileTimeOnly} import scala.meta._ @compileTimeOnly("hello") class hello extends

    StaticAnnotation { inline def apply(defn: Any): Any = meta { defn match { case d: Defn.Def => val newBody = q"""{ println("hello") ${d.body} }""" d.copy(body = newBody) case _ => abort("annotate only def") } } } def͡Όͳ͚Ε͹ࣦഊ
  36.  @hello def say() = { println("world") } class hello

    extends StaticAnnotation { inline def apply(defn: Any): Any = meta { defn match { case d: Defn.Def => val newBody = q"""{ println("hello") ${d.body} }""" d.copy(body = newBody) case _ => abort("annotate only def") } } }
  37.  @hello def say() = { println("world") } class hello

    extends StaticAnnotation { inline def apply(defn: Any): Any = meta { defn match { case d: Defn.Def => val newBody = q"""{ println("hello") ${d.body} }""" d.copy(body = newBody) case _ => abort("annotate only def") } } }
  38.  object TimeLoggingApp extends App { def heavy(n: Long): Long

    = { Thread.sleep(n) n } println(heavy(100)) }
  39.  object TimeLoggingApp extends App { @TimeLogging def heavy(n: Long):

    Long = { Thread.sleep(n) n } println(heavy(100)) }
  40.  [heavy]tracking time:101 ms 100 object TimeLoggingApp extends App {

    @TimeLogging def heavy(n: Long): Long = { Thread.sleep(n) n } println(heavy(100)) }
  41.  class TimeLogging extends StaticAnnotation { inline def apply(defn: Any):

    Any = meta { defn match { case d @ Defn.Def(_, name, _, _, _, body) => def s = Lit.String.apply _ val newBody = q""" val start = System.nanoTime() val result = $body val end = System.nanoTime() println(${s(s"[${name.value}]tracking time:")} + ((end - start) / ${Lit.Long(1000000)}) + ${s(" ms")}) result """ d.copy(body = newBody) case _ => abort("annotate only function!") } } }
  42.  class TimeLogging extends StaticAnnotation { inline def apply(defn: Any):

    Any = meta { defn match { case d @ Defn.Def(_, name, _, _, _, body) => def s = Lit.String.apply _ val newBody = q""" val start = System.nanoTime() val result = $body val end = System.nanoTime() println(${s(s"[${name.value}]tracking time:")} + ((end - start) / ${Lit.Long(1000000)}) + ${s(" ms")}) result """ d.copy(body = newBody) case _ => abort("annotate only function!") } } } લޙͰ࣌ؒऔಘ
  43.  class TimeLogging extends StaticAnnotation { inline def apply(defn: Any):

    Any = meta { defn match { case d @ Defn.Def(_, name, _, _, _, body) => def s = Lit.String.apply _ val newBody = q""" val start = System.nanoTime() val result = $body val end = System.nanoTime() println(${s(s"[${name.value}]tracking time:")} + ((end - start) / ${Lit.Long(1000000)}) + ${s(" ms")}) result """ d.copy(body = newBody) case _ => abort("annotate only function!") } } } ग़ྗ͍ͯ͠Δ͚ͩ
  44.  @Length(min = 1, max = 10) val ok =

    1 to 10 @Length(min = 100) val ng = 1 to 10
  45.  requirement failed: ng size is invalid. actual: 10, min:

    100 @Length(min = 1, max = 10) val ok = 1 to 10 // OK @Length(min = 100) val ng = 1 to 10 // NG
  46.  q""" def msg(min: Int, max: Int, resultSize: Int): String

    = { val minCond = { if (min <= 0) "" else "min: " + min } val maxCond = { if (max <= 0) "" else "max: " + max } val res = ${s(name.syntax)} + " size is invalid. " + "actual: " + resultSize + ", " + minCond + maxCond res } def minValid(min: Int, resultSize: Int): Boolean = if (min <= 0) true else min <= resultSize def maxValid(max: Int, resultSize: Int): Boolean = if (max <= 0) true else max >= resultSize val result = $term val size: Int = result.size require(minValid(${i(min)}, size) && maxValid(${i(max)}, size), msg(${i(min)}, ${i(max)}, size)) result """
  47.  q""" def msg(min: Int, max: Int, resultSize: Int): String

    = { val minCond = { if (min <= 0) "" else "min: " + min } val maxCond = { if (max <= 0) "" else "max: " + max } val res = ${s(name.syntax)} + " size is invalid. " + "actual: " + resultSize + ", " + minCond + maxCond res } def minValid(min: Int, resultSize: Int): Boolean = if (min <= 0) true else min <= resultSize def maxValid(max: Int, resultSize: Int): Boolean = if (max <= 0) true else max >= resultSize val result = $term val size: Int = result.size require(minValid(${i(min)}, size) && maxValid(${i(max)}, size), msg(${i(min)}, ${i(max)}, size)) result """ ৚݅൑ఆΛࠩ͠ࠐΜͰ͍Δ requireͳͷͰ࣮ߦ࣌ྫ֎
  48.  class MyClass(val n: Int, val s: String) object UnapplyApp

    extends App { val target = new MyClass(n = 100, s = "hoge") }
  49.  @Unapply class MyClass(val n: Int, val s: String) object

    UnapplyApp extends App { val target = new MyClass(n = 100, s = "hoge") }
  50.  @Unapply class MyClass(val n: Int, val s: String) object

    UnapplyApp extends App { val target = new MyClass(n = 100, s = "hoge") target match { case MyClass(n, s) => println(s"$n, $s") case _ => sys.error("out!") } }
  51.  100, hoge @Unapply class MyClass(val n: Int, val s:

    String) object UnapplyApp extends App { val target = new MyClass(n = 100, s = "hoge") target match { case MyClass(n, s) => println(s"$n, $s") case _ => sys.error("out!") } }
  52.  @compileTimeOnly("not expanded") class Unapply extends scala.annotation.StaticAnnotation { inline def

    apply(defn: Any): Any = meta { defn match { // companion object exists case Term.Block( Seq(cls @ Defn.Class(_, name, _, ctor, _), companion: Defn.Object)) => val newCompanion = Unapply.insert(cls)(Some(companion)) Term.Block(Seq(cls, newCompanion)) // companion object does not exists case cls @ Defn.Class(_, name, _, ctor, _) => val newCompanion = Unapply.insert(cls)(None) Term.Block(Seq(cls, newCompanion)) case _ => println(defn.structure) abort("@Unapply must annotate a class.") } } } object Unapply extends CompanionMethodHelper { override protected val METHOD_NAME: String = "unapply" override protected def create(cls: Defn.Class)(companionOpt: Option[Defn.Object]): Defn.Def = { val (name, paramss) = (cls.name, cls.ctor.paramss) val argName = Term.Name("arg") val inputParam: Seq[Seq[Term.Param]] = { val param: Term.Param = Term.Param(Nil, argName, Some(name), None) (param :: Nil) :: Nil } val resultParam: Type = { val types: Seq[Type] = paramss.flatMap { _.collect { case Term.Param(_ :: Mod.ValParam() :: Nil, _, Some(typeArg), _) => // private val n: Int Type.Name(typeArg.syntax) case Term.Param(_ :: Mod.VarParam() :: Nil, _, Some(typeArg), _) => // private var n: Int Type.Name(typeArg.syntax) case Term.Param(Mod.ValParam() :: Nil, _, Some(typeArg), _) => // val n: Int Type.Name(typeArg.syntax) case Term.Param(Mod.VarParam() :: Nil, _, Some(typeArg), _) => // var n: Int Type.Name(typeArg.syntax) case x => println(s"invalid paramss: $paramss") abort(s"non-accessible constructor exists. `unapply` always returns None. cause => $x") }} val tupled = Type.Tuple(types) Type.Apply(Type.Name("Option"), tupled :: Nil) } val body: Term = { val select: Seq[Term.Select] = paramss.flatMap { _.map { param => Term.Select(argName, Term.Name(param.name.value)) } } Term.Block(q"Some((..$select))" :: Nil) } Defn.Def(Nil, Term.Name("unapply"), Nil, inputParam, Some(resultParam), body) } } private[petitviolet] trait CompanionMethodHelper { protected val METHOD_NAME: String protected def create(cls: Defn.Class)(companionOpt: Option[Defn.Object]): Defn.Def def insert(cls: Defn.Class)(companionOpt: Option[Defn.Object]): Defn.Object = { def method = create(cls)(companionOpt) companionOpt map { companion => val stats = companion.templ.stats getOrElse Nil if (alreadyDefined(stats)) companion else companion.copy(templ = companion.templ.copy(stats = Some(method +: stats))) } getOrElse { q"object ${Term.Name(cls.name.value)} { $method }" } } private def alreadyDefined(stats: Seq[Stat]): Boolean = stats.exists { _.syntax.contains(s"def $METHOD_NAME") } }
  53.  class Unapply extends scala.annotation.StaticAnnotation { inline def apply(defn: Any):

    Any = meta { defn match { // companion object exists case Term.Block( Seq(cls @ Defn.Class(_, name, _, ctor, _), companion: Defn.Object)) => val newCompanion = Unapply.insert(cls)(Some(companion)) Term.Block(Seq(cls, newCompanion)) // companion object does not exists case cls @ Defn.Class(_, name, _, ctor, _) => val newCompanion = Unapply.insert(cls)(None) Term.Block(Seq(cls, newCompanion)) case _ => println(defn.structure) abort("@Unapply must annotate a class.") } } }
  54.  class Unapply extends scala.annotation.StaticAnnotation { inline def apply(defn: Any):

    Any = meta { defn match { // companion object exists case Term.Block( Seq(cls @ Defn.Class(_, name, _, ctor, _), companion: Defn.Object)) => val newCompanion = Unapply.insert(cls)(Some(companion)) Term.Block(Seq(cls, newCompanion)) // companion object does not exists case cls @ Defn.Class(_, name, _, ctor, _) => val newCompanion = Unapply.insert(cls)(None) Term.Block(Seq(cls, newCompanion)) case _ => println(defn.structure) abort("@Unapply must annotate a class.") } } } ίϯύχΦϯΦϒδΣΫτ͕͋Ε͹ Classఆ͕ٛTerm.BlockʹͳΔ ίϯύχΦϯΦϒδΣΫτ͕ͳ͚Ε͹ ClassఆٛͷΈ
  55.  @compileTimeOnly("not expanded") class Unapply extends scala.annotation.StaticAnnotation { inline def

    apply(defn: Any): Any = meta { defn match { // companion object exists case Term.Block( Seq(cls @ Defn.Class(_, name, _, ctor, _), companion: Defn.Object)) => val newCompanion = Unapply.insert(cls)(Some(companion)) Term.Block(Seq(cls, newCompanion)) // companion object does not exists case cls @ Defn.Class(_, name, _, ctor, _) => val newCompanion = Unapply.insert(cls)(None) Term.Block(Seq(cls, newCompanion)) case _ => println(defn.structure) abort("@Unapply must annotate a class.") } } } object Unapply extends CompanionMethodHelper { override protected val METHOD_NAME: String = "unapply" override protected def create(cls: Defn.Class)(companionOpt: Option[Defn.Object]): Defn.Def = { val (name, paramss) = (cls.name, cls.ctor.paramss) val argName = Term.Name("arg") val inputParam: Seq[Seq[Term.Param]] = { val param: Term.Param = Term.Param(Nil, argName, Some(name), None) (param :: Nil) :: Nil } val resultParam: Type = { val types: Seq[Type] = paramss.flatMap { _.collect { case Term.Param(_ :: Mod.ValParam() :: Nil, _, Some(typeArg), _) => // private val n: Int Type.Name(typeArg.syntax) case Term.Param(_ :: Mod.VarParam() :: Nil, _, Some(typeArg), _) => // private var n: Int Type.Name(typeArg.syntax) case Term.Param(Mod.ValParam() :: Nil, _, Some(typeArg), _) => // val n: Int Type.Name(typeArg.syntax) case Term.Param(Mod.VarParam() :: Nil, _, Some(typeArg), _) => // var n: Int Type.Name(typeArg.syntax) case x => println(s"invalid paramss: $paramss") abort(s"non-accessible constructor exists. `unapply` always returns None. cause => $x") }} val tupled = Type.Tuple(types) Type.Apply(Type.Name("Option"), tupled :: Nil) } val body: Term = { val select: Seq[Term.Select] = paramss.flatMap { _.map { param => Term.Select(argName, Term.Name(param.name.value)) } } Term.Block(q"Some((..$select))" :: Nil) } Defn.Def(Nil, Term.Name("unapply"), Nil, inputParam, Some(resultParam), body) } } private[petitviolet] trait CompanionMethodHelper { protected val METHOD_NAME: String protected def create(cls: Defn.Class)(companionOpt: Option[Defn.Object]): Defn.Def def insert(cls: Defn.Class)(companionOpt: Option[Defn.Object]): Defn.Object = { def method = create(cls)(companionOpt) companionOpt map { companion => val stats = companion.templ.stats getOrElse Nil if (alreadyDefined(stats)) companion else companion.copy(templ = companion.templ.copy(stats = Some(method +: stats))) } getOrElse { q"object ${Term.Name(cls.name.value)} { $method }" } } private def alreadyDefined(stats: Seq[Stat]): Boolean = stats.exists { _.syntax.contains(s"def $METHOD_NAME") } } • ίϯετϥΫλ͔ΒϑΟʔϧυ໊Λऔಘ • unapplyͷ࣮૷Ͱඞཁ • ίϯύχΦϯΦϒδΣΫτ͕ఆٛࡁΈ͔Ͳ͏͔ ΛνΣοΫ • ͋Ε͹unapplyͷ௥Ճ • ͳ͚Ε͹unapplyΛ࣋ͭίϯύχΦϯΦϒ δΣΫτΛఆٛ • annotationͨ͠ΫϥεΛίϯύχΦϯΦϒδΣ ΫτΛ࣋ͭΫϥεͰஔ͖׵͑Δ
  56.  trait MyService { def double(i: Int): Int = i

    * 2 } object MyServiceApp extends App { }
  57.  trait MyService { def double(i: Int): Int = i

    * 2 } @MixIn[MyService](new MyService {}) object MyServiceApp extends App { println(myService.double(100)) }
  58.  trait MyService { def double(i: Int): Int = i

    * 2 } @MixIn[MyService](new MyService {}) object MyServiceApp extends App { println(myService.double(100)) }
  59.  trait MyService { def double(i: Int): Int = i

    * 2 } @MixIn[MyService](new MyService {}) object MyServiceApp extends App { println(myService.double(100)) }
  60.  200 trait MyService { def double(i: Int): Int =

    i * 2 } @MixIn[MyService](new MyService {}) object MyServiceApp extends App { println(myService.double(100)) }
  61.  @compileTimeOnly("@MixIn not expanded") class MixIn[T](impl: T) extends scala.annotation.StaticAnnotation {

    inline def apply(defn: Any): Any = meta { val (injectionType: Type.Name, impl: Term) = this match { case Term.New(Template(_, Seq( Term.Apply( Term.ApplyType(_, Seq(typeName)), Seq(implArg: Term.Arg) )), _, _)) => (typeName, implArg) case _ => println(this.structure) abort("invalid parameters") } // to inject a field has implementation val addField = MixIn.implementationToAdd(injectionType, impl) defn match { case [email protected](_, _, _, _, template) => val templateStats: Seq[Stat] = addField +: template.stats.getOrElse(Nil) cls.copy(templ = template.copy(stats = Some(templateStats))) case [email protected](_, _, template) => val templateStats: Seq[Stat] = addField +: template.stats.getOrElse(Nil) obj.copy(templ = template.copy(stats = Some(templateStats))) case [email protected](_, _, _, _, template) => val templateStats: Seq[Stat] = addField +: template.stats.getOrElse(Nil) trt.copy(templ = template.copy(stats = Some(templateStats))) case _ => println(this.structure) abort("invalid annotating") } } } object MixIn { private[metas] def implementationToAdd(typeName: Type.Name, impl: Term): Defn.Val = { val fieldName = { val _tpeName = typeName.value val valName = _tpeName.head.toLower + _tpeName.tail Pat.Var.Term(Term.Name(valName)) } q"val $fieldName: $typeName = $impl" } }
  62.  @compileTimeOnly("@MixIn not expanded") class MixIn[T](impl: T) extends scala.annotation.StaticAnnotation {

    inline def apply(defn: Any): Any = meta { val (injectionType: Type.Name, impl: Term) = this match { case Term.New(Template(_, Seq( Term.Apply( Term.ApplyType(_, Seq(typeName)), Seq(implArg: Term.Arg) )), _, _)) => (typeName, implArg) case _ => println(this.structure) abort("invalid parameters") } // to inject a field has implementation val addField = MixIn.implementationToAdd(injectionType, impl) defn match { case [email protected](_, _, _, _, template) => val templateStats: Seq[Stat] = addField +: template.stats.getOrElse(Nil) cls.copy(templ = template.copy(stats = Some(templateStats))) case [email protected](_, _, template) => val templateStats: Seq[Stat] = addField +: template.stats.getOrElse(Nil) obj.copy(templ = template.copy(stats = Some(templateStats))) case [email protected](_, _, _, _, template) => val templateStats: Seq[Stat] = addField +: template.stats.getOrElse(Nil) trt.copy(templ = template.copy(stats = Some(templateStats))) case _ => println(this.structure) abort("invalid annotating") } } } object MixIn { private[metas] def implementationToAdd(typeName: Type.Name, impl: Term): Defn.Val = { val fieldName = { val _tpeName = typeName.value val valName = _tpeName.head.toLower + _tpeName.tail Pat.Var.Term(Term.Name(valName)) } q"val $fieldName: $typeName = $impl" } } • annotationʹର͢ΔܕύϥϝʔλͱίϯετϥΫ λͷtreeΛऔಘ • annotationͨ͠Ϋϥεʹରͯ͠ϑΟʔϧυΛ௥Ճ • ϑΟʔϧυͷܕͱbodyʹલड़ͷ஋Λ༩͑Δ
  63. 

  64.  200 trait MyService { def double(i: Int): Int =

    i * 2 } @MixIn[MyService](new MyService {}) object MyServiceApp extends App { println(myService.double(100)) }
  65.  trait MyService { def double(i: Int): Int = i

    * 2 } @MixIn[MyService](new MyService {}) object MyServiceApp extends App { println(myService.double(100)) }
  66.  class MixIn[T](impl: T) extends StaticAnnotation { inline def apply(defn:

    Any): Any = meta { println(s"structure: ${this.structure}") defn } } @MixIn[MyService](new MyService {}) object MyServiceApp extends App { println(myService.double(100)) }
  67.  class MixIn[T](impl: T) extends StaticAnnotation { inline def apply(defn:

    Any): Any = meta { println(s"structure: ${this.structure}") defn } } @MixIn[MyService](new MyService {}) object MyServiceApp extends App { println(myService.double(100)) }
  68. 

  69.  @compileTimeOnly("@MixIn not expanded") class MixIn[T](impl: T) extends scala.annotation.StaticAnnotation {

    inline def apply(defn: Any): Any = meta { val (injectionType: Type.Name, impl: Term) = this match { case Term.New(Template(_, Seq( Term.Apply( Term.ApplyType(_, Seq(typeName)), Seq(implArg: Term.Arg) )), _, _)) => (typeName, implArg) case _ => println(this.structure) abort("invalid parameters") } …
  70.  @compileTimeOnly("@MixIn not expanded") class MixIn[T](impl: T) extends scala.annotation.StaticAnnotation {

    inline def apply(defn: Any): Any = meta { val (injectionType: Type.Name, impl: Term) = this match { case Term.New(Template(_, Seq( Term.Apply( Term.ApplyType(_, Seq(typeName)), Seq(implArg: Term.Arg) )), _, _)) => (typeName, implArg) case _ => println(this.structure) abort("invalid parameters") } … thisʹର͢ΔύλʔϯϚον ίϯετϥΫλͷ`impl`͸௚઀औΕͳ͍
  71.  @compileTimeOnly("@MixIn not expanded") class MixIn[T](impl: T) extends scala.annotation.StaticAnnotation {

    inline def apply(defn: Any): Any = meta { val (injectionType: Type.Name, impl: Term) = this match { case Term.New(Template(_, Seq( Term.Apply( Term.ApplyType(_, Seq(typeName)), Seq(implArg: Term.Arg) )), _, _)) => (typeName, implArg) case _ => println(this.structure) abort("invalid parameters") } … ܕύϥϝʔλɾίϯετϥΫλΛऔಘ
  72.  @compileTimeOnly("@MixIn not expanded") class MixIn[T](impl: T) extends scala.annotation.StaticAnnotation {

    inline def apply(defn: Any): Any = meta { val (injectionType: Type.Name, impl: Term) = this match { case Term.New(Template(_, Seq( Term.Apply( Term.ApplyType(_, Seq(typeName)), Seq(implArg: Term.Arg) )), _, _)) => (typeName, implArg) case _ => println(this.structure) abort("invalid parameters") } … ͏·͍͔͘ͳ͍࣌͸ println(this.structure)͢Δͱྑ͍ ίϯύΠϧ࣌ͷϝοηʔδͱͯ͠දࣔ͞ΕΔ
  73. 既存のマクロの問題点 • macro実装/コンパイラ内部の知識が必要になる • macroの実装を知っていないと名前解決に失敗したりする • 英数字でない名前をmangleしている、とか • シグネチャ •

    macroのための実装を別に用意する • 同じ仮引数名にしないといけない • エコシステム • ツールのサポートが弱い  http://docs.scala-lang.org/sips/pending/inline-meta.html
  74. inline化  class hello extends StaticAnnotation { inline def apply(defn:

    Any): Any = meta { defn match { case d: Defn.Def => val newBody = q"""{ println("hello") ${d.body} }""" d.copy(body = newBody) case _ => abort("annotate only def") } } }
  75. inlineキーワード • インライン化 • コンパイル時に定数化 • 関数呼び出しを削除 • ちゃんと型チェックしていて推論も可能 •

    `@inline`アノテーションは保証しない • ベストエフォート的なやつ • deprecatedになる予定(?) 
  76. metaキーワード  class hello extends StaticAnnotation { inline def apply(defn:

    Any): Any = meta { defn match { case d: Defn.Def => val newBody = q"""{ println("hello") ${d.body} }""" d.copy(body = newBody) case _ => abort("annotate only def") } } }
  77. アーキテクチャ • 分割されたAPI • stateless • syntactic • semantic •

    操作と必要な状態に応じて分割されている 
  78. Syntactic API • 構文に関するAPI • QuasiQuotesやpretty print • 例えばScalaのバージョンが状態となる 

    import scala.meta._ { import scala.meta.dialects.Scala211 "<hello />".parse[Term].get } { import scala.meta.dialects.Dotty "<hello />".parse[Term].get }
  79. Syntactic API • 構文に関するAPI • QuasiQuotesやpretty print • 例えばScalaのバージョンが状態となる 

    import scala.meta._ { import scala.meta.dialects.Scala211 "<hello />".parse[Term].get } { import scala.meta.dialects.Dotty "<hello />".parse[Term].get } ੒ޭ ྫ֎ൃੜ
  80.  object mirror { def show(str: String) = println(str) }

    object mirrorApp extends App { implicit val m = Mirror() m.sources.foreach { _.collect { case ref @ Term.Name("println") => println(s"symbol: ${ref.symbol}") }} }
  81.  object mirror { def show(str: String) = println(str) }

    object mirrorApp extends App { implicit val m = Mirror() m.sources.foreach { _.collect { case ref @ Term.Name("println") => println(s"symbol: ${ref.symbol}") }} }
  82.  object mirror { def show(str: String) = println(str) }

    object mirrorApp extends App { implicit val m = Mirror() m.sources.foreach { _.collect { case ref @ Term.Name("println") => println(s"symbol: ${ref.symbol}") }} } [info] symbol: _root_.scala.Predef.println(Ljava/lang/Object;)V. γϯϘϧ৘ใͷऔಘ
  83. その前に現状 • v1.8.0まではmacro annotationのみ • 色々とツールが作られている • http://scalameta.org/#BuiltwithScalameta • scalafmt:

    コードフォーマッタ • syntacticな操作 • scalafix: scalaの自動変換ツール(dotty化とか) • semanticな操作 
  84. 

  85. 

  86.