哲学原理Scala Macros - 元编程 Metaprogramming with Def Macros

by admin on 2018年10月21日

 

作者:苏清涛(微信号:charitableman)

1   def testMethod[T]: Double = {
2     val x = 2.0 + 2.0
3     Math.pow(x, x)
4   }
5 
6   def testMethodWithArgs(x: Double, y: Double) = {
7     val z = x + y
8     Math.pow(z,z)
9   }

即便于自身萌生此意后抢,某日,一恋人慎重地指向自我说:“XXX好像特别佩服你什么,这个家里擅长炒股,很能够挣,你若是娶亲了其,就不要费心思考虑赚钱的工作了,这样尽管足以安心地讲话哲学了。”

 1 import scala.language.experimental.macros
 2 import scala.reflect.macros.whitebox.Context
 3 
 4 trait CaseClassMapConverter[C] {
 5   def toMap(c: C): Map[String,Any]
 6   def fromMap(m: Map[String,Any]): C
 7 }
 8 object CaseClassMapConverter {
 9   implicit def Materializer[C]: CaseClassMapConverter[C] = macro converterMacro[C]
10   def converterMacro[C: c.WeakTypeTag](c: Context): c.Tree = {
11     import c.universe._
12 
13     val tpe = weakTypeOf[C]
14     val fields = tpe.decls.collectFirst {
15       case m: MethodSymbol if m.isPrimaryConstructor => m
16     }.get.paramLists.head
17 
18     val companion = tpe.typeSymbol.companion
19     val (toParams,fromParams) = fields.map { field =>
20     val name = field.name.toTermName
21     val decoded = name.decodedName.toString
22     val rtype = tpe.decl(name).typeSignature
23 
24       (q"$decoded -> t.$name", q"map($decoded).asInstanceOf[$rtype]")
25 
26     }.unzip
27 
28     q"""
29        new CaseClassMapConverter[$tpe] {
30         def toMap(t: $tpe): Map[String,Any] = Map(..$toParams)
31         def fromMap(map: Map[String,Any]): $tpe = $companion(..$fromParams)
32        }
33       """
34   }
35 }

白富美在羁押罢那篇写王小波的文章后,感慨说这个吃软饭的先生“实在太迷人了”,她还上了千篇一律句:“不过,我当您于他还可爱。”随后,白富美以咨询我:“李银河是怎么的人口,你了解也?”“不清楚,你问问这关系吧?”白富美说:“我先行了解一下李银河的特质,就知道王小波这样的老公喜欢什么样的太太了;这样,万一自家之后遇到一个王小波一样的丈夫,我就算得自身炫耀一下了。”这词话说得真可爱,哈哈。

1 def appl(c: Context)(args: c.Tree*) = {
2     import c.universe._
3     val arglist = args.toList
4     q"new FreeUser(..$arglist)"
5   }

老公撒娇就同沾实在十分重大,看一个男人爱非爱而,有某些即是设拘留他跟未跟公撒娇,因为丈夫撒娇和内撒娇不均等,大多数汉子就会以及自己不过信任和最靠之家里撒娇,就如男人单独当妈妈那里才是小朋友一样,虽然一个男人撒娇未必就是表示他好您,但是一个女婿只要无跟而撒娇的语句,或者突然不跟你撒娇了,那若不过若小心了,他蛮可能不再爱您,也非欲您了。
一个爱人一旦大会撒娇,那真的是相同件超级致命的秘密武器,有时候更看起颇man的爱人,撒起娇来针对家里之杀伤力越充分,就比如王小波那样。

 

要洪晃所说之“吃软米饭”,这个语境中的“被人养”或“我留下你”,则是叫人看做宠物(虽则自己明知“宠物”一歌词不太合适,有以人物化的存疑,但自身骨子里找不起更准确的辞藻了,sorry)来养、当做孩子来养,或者被当心灵顾问来养——也就是说,“我留下你”的无比根本由或者唯一原因,在于“我看您十分纯情还是可尊敬”,“我留给你”,可能是同色欲毫不相干的。有篇题吗《撒娇大王王小波》的日志,很明亮地证实了,王小波这种其貌不扬、身体也略好的先生之所以能以李银河那里吃到软饭,就是为他挺可喜。

      case q"$mods class $cname[..$tparams] $ctorMods(..$params) extends Animal with ..$parents {$self => ..$stats}" =>

白富美:“等自长大了,要是自身发生钱了,我哪怕来保证尔,天天帮自己谈话故事,天天听你开口道理。”

 

这么多年过去了,我都老少以及白富美联系了,只是有时还会见说几词话,但它们曾经开出的那么张空头支票,却予以了自无限的力量。每当我于读书及劳作达保有懈怠的时光,我哪怕迷迷糊糊暗问自己:“凭你及时幅鸟样子,你生出吃软饭的本事也?人家白富美任啥来包养你吗?
”问了之后,我读时即愈充满热情和动力了。

自打compiling greeting
…这条提拔我们好汲取在编译demo目录下源代码文件的长河被应该运算了greetingMacro函数。测试运行后产生结果:

当,我深深地解,以自己某个面前之实力(既未“才子”,又无“肌肉男”),是凭着不齐软饭的;即使偶尔侥幸吃到了,也开不顶心里安理得,免不了以别人面前抬不自峰来。想踹上软饭之路,那不过真难,难于上青天;所以呢,我不能不好好学习天天向上,为了促成“吃软米饭”这个好而不懈斗争!

Hello, I'm a Dog and my name is Goldy wangwang...
Hello, I'm a Cat and my name is Kitty miaomiao...

Process finished with exit code 0

那会儿的本身,孤陋寡闻,从未听了洪晃这个人名,但迅即并无伤我吃其思想所掀起。我立就百度出了它们底博文《吃软饭》,其中最中心的几句子是:

 1 object annotMethodDemo extends App {
 2 
 3   @Benchmark
 4   def testMethod[T]: Double = {
 5     //val start = System.nanoTime()
 6 
 7     val x = 2.0 + 2.0
 8     Math.pow(x, x)
 9 
10     //val end = System.nanoTime()
11     //println(s"elapsed time is: ${end - start}")
12   }
13   @Benchmark
14   def testMethodWithArgs(x: Double, y: Double) = {
15     val z = x + y
16     Math.pow(z,z)
17   }
18 
19   testMethod[String]
20   testMethodWithArgs(2.0,3.0)
21 
22 
23 }

立段话是自身从《撒娇大王王小波》一和平遭遇摘录出来的,并且,感觉好是“躺着啊中枪”。
有同等坏,我对一个对象说:“你平常本着己的态度,就比如是在对照自己夫人的均等漫长狗!”她觉得特别委屈、很冤枉:“你怎么好这样说自己吗?我对你哪里不好了?”然后,我又补了同等词:“不是当一长长的普通的狗,而是同样长条宠物狗!”她情不自禁笑了。

通过Def
Macros在编译过程中自动生成apply和unapply,它们各自对应了函数调用:

“没有吃软饭的老公便不曾Matisse的《戴帽子的夫人》,也没有一个受《断背山》的影,也非可能发啊马克思主义。
所以千万别小看吃软饭的男人。吃不达标不得不证实您自己不足够有力。”

运算greeting实际上是调用了greetingMacro中的macro实现代码。上面这个例子使用了最好基础之Scala
Macro编程模式。注意这事例里套数greetingMacro的参数c:
Context和于函数内部代码中reify,splice的调用:由于Context是个动态函数接口,每个实例都有所不同。对于大型的macro实现函数,可能会见调用到外同样会用到Context的提携函数(helper
function),容易并发Context实例不兼容问题。另外reify和splice可以说凡是不过原始之AST操作函数。我们当脚是事例里用了流行的模式以及方:

软饭之路遥远而修远兮,吾将上下而求索。

 1 def uapl(c: Context)(u: c.Tree) = {
 2     import c.universe._
 3     val params = u.tpe.members.collectFirst {
 4       case m: MethodSymbol if m.isPrimaryConstructor => m.asMethod
 5     }.get.paramLists.head.map {p => p.asTerm.name.toString}
 6 
 7     val (qget,qdef) = params.length match {
 8       case len if len == 0 =>
 9         (List(q""),List(q""))
10       case len if len == 1 =>
11         val pn = TermName(params.head)
12         (List(q"def get = u.$pn"),List(q""))
13       case  _ =>
14         val defs = List(q"def _1 = x",q"def _2 = x",q"def _3 = x",q"def _4 = x")
15         val qdefs = (params zip defs).collect {
16           case (p,d) =>
17             val q"def $mname = $mbody" = d
18             val pn = TermName(p)
19             q"def $mname = u.$pn"
20         }
21         (List(q"def get = this"),qdefs)
22     }
23 
24       q"""
25         new {
26           class Matcher(u: User) {
27             def isEmpty = false
28             ..$qget
29             ..$qdef
30           }
31           def unapply(u: User) = new Matcher(u)
32         }.unapply($u)
33       """
34   }
35 }

本屌丝说自己基础差、底子薄,白富美“安慰”我说:“大叔不要担心,越老越来价。你40岁了,搞不好我不怕来即你(傍大款)了。”

 

某年某月某日,我遇见了白富美,或者,更准地说,是白富美遇到了自我。那同样年,她还充分有些,算是个姑娘(但连无是“无知少女”);而我,已经老了。不知其本来就是是个“大叔控”呢,还是以认识自己事后才成为了只特别叔控。也非理解是当认识自我从此的第多少天,她说自己现在求学啊的远非动力,需要摸索个精神支柱。

圆的Macro实现源代码如下:

软饭可以吃,但是,吃软饭也是一旦提条件的,并非无任如何的软饭都可以吃;吃软饭,仍然必须以情啊前提。始作俑者不发话了,于是自己以问道:“莫非,你的意是,建议我吃XXX包养起来,然后再次用她发之薪饷来查找另外一个家里说哲学?”

于这例子里我们管macro实现函数放入一个以Context为参数的class。我们好把有应用Context的函数都摆在这个class里面大家共同用统一的Context实例。quasiquotes是时髦的AST操作函数集,可以重新有利于灵活地控制AST的出、表达式还原等。这个tell
macro的调用还是同的:

诚然想不通,我者从鄙视“以妞养妞”做法的口,竟然能说生一番这么难看的话语来;虽为笑话,也足以看得出,我之不知不觉中,还是时有发生那么一点点卑鄙的。我之不要脸的想法,在得到了于场男性支持的又,也遭遇了与女性的明确鄙视。对于此等惨绝人寰的邪恶念头,我未盼得到任何人的超生——任何宽恕,都见面给自身累忍受良心的磨难。

下面是macro
appl的实现:

本屌丝:“那好,我还盼望着为妻子包养呢。哈哈。真不使脸。”

在上头这事例里usr”???”的usr是一个pattern和extractor
object。与平常的string
interpolation不同的凡usr不是一个艺术(method),而是一个靶(object)。这是由于模式匹配中的unapply必须在一个extractor
object内,所以usr是只object。我们懂得一个object加上其的apply可以当method来调用。也就是说要当usr
object中贯彻了apply就可据此usr(???)作为method使用,如下:

齐段的对话中,尽管其它一样当事人也支持“吃软米饭”,但那种软饭,本质上是怀念使他人对团结的情,是损公肥私自利;其论理是“我未易于你的口,但为你的钱,我还是肯同你生在一起”。故而,对这种观念,我们应有坚决抵制;当然,如果让使用的如出一辙在自愿“犯贱”的话,那其他当别论。

 运算结果如下:

那么同样年,白富美对本屌丝说:等自家长大了包养你吧——做一个“有资格吃软饭的总人口”

俺们看:同样还是经过quasiquote进行AST模式拆分:

2.逻辑思维能力正常的人,一定看得出本文中某些事情有的工夫吃我被篡改了,我想狡辩一下:哥穿越了!

 

只有真正发生本事的丁才会吃软饭。首先人如果精彩,这没得说了;谈吐要幽默,要产得矣厨房,上得矣大厅;床上而风情万种,杂技、舞蹈及瑜伽,什么功夫都未可知赢得下。这容易吗?你行吧你?所以别吃不达到软饭说软米饭酸,真来本事的总人口,都以吃软饭。
提倡吃软饭对社会发出积极意义,首先会迎刃而解许多就业问题。我们管一些就业机会空出来,给那些没有本事吃软饭的口不是生好与否?

足见见:我们要用quasiquote来分拆被诠释的方法,然后又就此quasiquote重现做是法子。在整合过程遭到追加了时截取和列印代码。下面这行是第一流的AST模式分拆(pattern
desctruction):

本屌丝:“好的。那我祝福您早点发财。”
同时,我在内心里偷偷念叨:玩笑话,切莫当真。

留意在编译HelloMacro.scala时发出的出口:

本屌丝何止是深受宠若惊,简直就是吓了一跳。这种暴殄天物、摧残祖国花朵的作业本身怎么能做呢?我眷恋逃避得远远的。白富美要求自我当QQ上针对其装“隐身可见”,我也尚未敢同意。最后,她说:“你无与伦比起码要伴随自己走得了高三、度过高考什么。你如是啊一样龙发生了女对象后不再理我,看自己岂把你们拆散!”这简直是“史上极度牛逼小三宣言”。小二尚未出现,“小三”却提前出现了,这确实是一个有时候。

 

备注:

 

2012年6月

 

自我说:“XXX是休是善赚钱,这个我连无关心;我实在关注的题材是:XXX可不可以和自己称哲学?”

前面大部分代码就是为形成List[Tree]
qget和qdef,最后结合一个一体化的quasiquote q””” new {…}”””。

自恃软饭,说得直白一点,也就是“被人养”。但这个“被人养”,又未同等于“被包养”:一般的话,“被包养”或“我包养你”只发生同重叠含义,就是让当做性奴隶来养,不管男人吃老婆包养,还是老婆吃男人包养,均是如此,并且双方权利义务关系甚明亮,即被包养者有满足雇主性欲之义务,而包养者享有在叫包养者身上发泄性欲的权利。

以上是Def
Macros的正规化兑现模式。基本原理是如此的:当编译器在编译modules遇到方法调用greeting(“john”)时见面进行函数符号解析、在mmacros里发现greeting是独macro,它的求实实现以greetingMacro函数里,此时编译器会运行greetingMacro函数并拿运算结果-一个AST调用替代表达式greeting(“john”)。注意编译器在运算greetingMacro时会以AST方式将参数person传入。由于当编译modules对象时索要运算greetingMacro函数,所以greetingMacro函数乃至全mmacros对象要是已经编译状态,这便意味着modules和mmacros必须各自以不同的源代码文件里,而且还要确保于编译modules前先行成功对mmacros的编译,我们得以打sbt设置文件build.sbt看到它们的干:

继之,我转载了洪晃这首博文,著名哲学男读过之后写道:“Oh
yeh,我尽快如吃上软米饭了。”受这个振奋,作为卢梭和王小波的铁杆粉丝,我这个根本没有了吃软饭想法的“守本分的人口”,竟然由此而起起了一个般没出息的宏伟目标:将团结养成一个出资格吃软饭的总人口

val someuser =  usr"$fname,$lname"

case usr"$first,$last" => println(s"hello $first $last")

在此之前,我对未来径直十分悲观的;自白富美做出了而包养我的应允以后,我就对未来满载了信心。

 

1.本文中的故事纯属虚构,是按屌丝捏造出意淫的,列为看官切莫当真;如有雷同,纯属巧合。
当然,如果您免得看最终的如出一辙段落声明是“不从自招”的话,那我啊无话可说。

      case q"$mods def $mname[..$tpes](...$args): $rettpe = { ..$stats }" => {...}

“安心吃软饭的老公都是大人物,真的。没有他们,我们的社会就是少了哲学家、艺术家、作家,一个尚无吃软米饭男人的社会,一定是一个免文明、没文化的社会。今天,大家都以说神州当代知识缺失有能力的创作,这同大部分丈夫都失去经商挣钱是起涉及之,经济这么增长,文化怎么能不晚降,不管男的,女之,所有心思都失去挖掘磨怎么赚钱钱了。”

所以这种办法将对象分拆成成需要之顶基本有。Macro
Annotation的贯彻源代码如下:

坏老之前的某个一样龙,偶然之中看了李银河的同样漫漫微博:“看到洪晃说有本事的人才吃软饭,说得很了不起,李安就是于家里养着,王小波以挺丰富一段时间靠自身留给在,对没有本事的丈夫来说,被人说吃软饭是绝充分之打击,只有有本事的先生才产生底气被留起来。小波和我说罢,巴尔扎克也是究竟起贵妇养的,证明了众多名特优之文学家艺术家都如此。”这个另类的看法这引起了自家之兴;并且,我这想到,卢梭似乎为一度出了同样段吃软饭的存。

 

俺们来看望这Macro的实现函数:macros/CaseClassConverter.scala

 

倘若本身想测试其运行所急需时的语可以在及时点儿只方式的里代码前设定开始时,然后以代码后截取完成时,完成时-开始日即运算所待的流年了,如下:

以上是macro
greeting的具体声明与实现。代码放在macros目录下的MacrosLibrary.scala里。首先须import
macros和Context。

1 object HelloMacro extends App {
2   import LibraryMacros._
3   greeting("john")
4 }

 

 

 1 import scala.annotation.StaticAnnotation
 2 import scala.language.experimental.macros
 3 import scala.reflect.macros.blackbox.Context
 4 
 5 class TalkingAnimal(val voice: String) extends StaticAnnotation {
 6   def macroTransform(annottees: Any*): Any = macro TalkingAnimal.implAnnot
 7 }
 8 
 9 object TalkingAnimal {
10   def implAnnot(c: Context)(annottees: c.Tree*): c.Tree = {
11     import c.universe._
12 
13     annottees.head match {
14       case q"$mods class $cname[..$tparams] $ctorMods(..$params) extends Animal with ..$parents {$self => ..$stats}" =>
15         val voice = c.prefix.tree match {
16           case q"new TalkingAnimal($sound)" => c.eval[String](c.Expr(sound))
17           case _ =>
18             c.abort(c.enclosingPosition,
19                     "TalkingAnimal must provide voice sample!")
20         }
21         val animalType = cname.toString()
22         q"""
23             $mods class $cname(..$params) extends Animal {
24               ..$stats
25               def sayHello: Unit =
26                 println("Hello, I'm a " + $animalType + " and my name is " + name + " " + $voice + "...")
27             }
28           """
29       case _ =>
30         c.abort(c.enclosingPosition,
31                 "Annotation TalkingAnimal only apply to Animal inherited!")
32     }
33   }
34 }

 

Hello john, the time is: Wed Nov 09 11:42:21 HKT 2016
Hello mary, it is: Wed Nov 09 11:42:20 HKT 2016

Process finished with exit code 0
 1 object Test extends App {
 2   import ExtractorMicros._
 3   val fname = "William"
 4   val lname = "Wang"
 5   val someuser =  usr"$fname,$lname"  //new FreeUser("William","Wang")
 6 
 7   someuser match {
 8     case usr"$first,$last" => println(s"hello $first $last")
 9   }
10 }

 

Hello john, the time is: Wed Nov 09 09:32:04 HKT 2016

Process finished with exit code 0
1   import ExtractorMicros._
2   val fname = "William"
3   val lname = "Wang"
4   val someuser =  usr"$fname,$lname"  //new FreeUser("William","Wang")
5 
6   someuser match {
7     case usr"$first,$last" => println(s"hello $first $last")
8   }

 

测试运算产生下的结果:

注解的调用示范代码如下:

 

 

调用示范代码:

在点这个implicit
Macros例子里引用了部分quasiquote语句(q”xxx”)。quasiquote是Scala
Macros的一个要害组成部分,主要代表了原先reflect
api中之reify功能,具备更精、方便灵活的拍卖AST功能。Scala Def
Macros还提供了Extractor Macros,结合Scala String
Interpolation和模式匹配来提供compile time的extractor
object生成。Extractor Macros的具体用例如下:

 1 import scala.annotation.StaticAnnotation
 2 import scala.language.experimental.macros
 3 import scala.reflect.macros.blackbox.Context
 4 
 5 class Benchmark extends StaticAnnotation {
 6   def macroTransform(annottees: Any*): Any = macro Benchmark.impl
 7 }
 8 object Benchmark {
 9   def impl(c: Context)(annottees: c.Tree*): c.Tree = {
10     import c.universe._
11 
12     annottees.head match {
13       case q"$mods def $mname[..$tpes](...$args): $rettpe = { ..$stats }" => {
14         q"""
15             $mods def $mname[..$tpes](...$args): $rettpe = {
16                val start = System.nanoTime()
17                val result = {..$stats}
18                val end = System.nanoTime()
19                println(${mname.toString} + " elapsed time in nano second = " + (end-start).toString())
20                result
21             }
22           """
23       }
24       case _ => c.abort(c.enclosingPosition, "Incorrect method signature!")
25     }
26 
27   }
28 }

 

反向把Map转成case
class:

 

 

重在通过q”new
FreeUser(arg1,arg2)”实现了个AST的构建。macro
uapl的贯彻相对复杂些,对quasiquote的采取会重新深入些。首先要规定项目的primary
constructor的参数数量与称号,然后经过quasiquote的模式匹配分解产生相应的sub-AST,再重新组合形成最终整的AST:

 

 

 1 import scala.language.experimental.macros
 2 import scala.reflect.macros.blackbox.Context
 3 import java.util.Date
 4 object LibraryMacros {
 5   def greeting(person: String): Unit = macro greetingMacro
 6 
 7   def greetingMacro(c: Context)(person: c.Expr[String]): c.Expr[Unit] = {
 8     import c.universe._
 9     println("compiling greeting ...")
10     val now = reify {new Date().toString}
11     reify {
12       println("Hello " + person.splice + ", the time is: " + new Date().toString)
13     }
14   }
15 }
 1   def tell(person: String): Unit = macro MacrosImpls.tellMacro
 2   class MacrosImpls(val c: Context) {
 3     import c.universe._
 4       def tellMacro(person: c.Tree): c.Tree = {
 5       println("compiling tell ...")
 6       val now = new Date().toString
 7       q"""
 8           println("Hello "+$person+", it is: "+$now)
 9         """
10     }
11   }
1   implicit class UserInterpolate(sc: StringContext) {
2     object usr {
3       def apply(args: String*): Any = macro UserMacros.appl
4       def unapply(u: User): Any = macro UserMacros.uapl
5     }
6   }
 1  def ccToMap[C: CaseClassMapConverter](c: C): Map[String,Any] =
 2     implicitly[CaseClassMapConverter[C]].toMap(c)
 3 
 4   case class Person(name: String, age: Int)
 5   case class Car(make: String, year: Int, manu: String)
 6 
 7   val civic = Car("Civic",2016,"Honda")
 8   println(ccToMap[Person](Person("john",18)))
 9   println(ccToMap[Car](civic))
10 
11 ...
12 Map(name -> john, age -> 18)
13 Map(make -> Civic, year -> 2016, manu -> Honda)

下面我们先行用个大概的例证来演示分析一下Def
Macros的基本原理和动用方法:

    Scala
Macros对scala函数库编程人员来说是相同桩不可或缺的编程工具,可以通过它们来缓解一些就此普通编程或者类层次编程(type
level programming)都心有余而力不足化解之题目,这是坐Scala
Macros可以直接指向程序开展改动。Scala
Macros的办事原理是在次编译时按编程人员的意志对同段先后开展改动产生有同段落新的程序。具体经过是:当编译器在针对程序进行项目验证(typecheck)时要发现Macro标记就见面拿之Macro的机能实现程序(implementation):一个语法树(AST,
Abstract Syntax
Tree)结构拉扯过来在Macro的岗位展开替代,然后于夫AST开始持续展开项目验证过程。

1 object modules {
2    greeting("john")
3  }
4  
5  object mmacros {
6    def greeting(person: String): Unit = macro greetingMacro
7    def greetingMacro(c: Context)(person: c.Expr[String]): c.Expr[Unit] = ...
8  }

 

下我们来探macro的切切实实贯彻方式:

Macros
Annotation(注释)是Def
Macro重要的力量部分。对一个靶,包括项目、对象、方法齐进行诠释意思是当源代码编译时对它们进行拓展修改甚至完全替换。比如我们下展示的法子注释(method
annotation):假设我们来脚两独办法:

Mac-Pro:learn-macro tiger-macpro$ sbt
[info] Loading global plugins from /Users/tiger-macpro/.sbt/0.13/plugins
[info] Loading project definition from /Users/tiger-macpro/Scala/IntelliJ/learn-macro/project
[info] Set current project to learn-macro (in build file:/Users/tiger-macpro/Scala/IntelliJ/learn-macro/)
> project demos
[info] Set current project to demos (in build file:/Users/tiger-macpro/Scala/IntelliJ/learn-macro/)
> compile
[info] Compiling 1 Scala source to /Users/tiger-macpro/Scala/IntelliJ/learn-macro/macros/target/scala-2.11/classes...
[info] 'compiler-interface' not yet compiled for Scala 2.11.8. Compiling...
[info]   Compilation completed in 7.876 s
[info] Compiling 1 Scala source to /Users/tiger-macpro/Scala/IntelliJ/learn-macro/demos/target/scala-2.11/classes...
compiling greeting ...
[success] Total time: 10 s, completed 2016-11-9 9:28:24
> 

留意最后一实施:demos
dependsOn(macros),因为咱们会将具备macros定义文件在macros目录下。

 1   def mapTocc[C: CaseClassMapConverter](m: Map[String,Any]) =
 2     implicitly[CaseClassMapConverter[C]].fromMap(m)
 3 
 4   val mapJohn = ccToMap[Person](Person("john",18))
 5   val mapCivic = ccToMap[Car](civic)
 6   println(mapTocc[Person](mapJohn))
 7   println(mapTocc[Car](mapCivic))
 8 
 9 ...
10 Person(john,18)
11 Car(Civic,2016,Honda)
 1 trait User {
 2   val fname: String
 3   val lname: String
 4 }
 5 
 6 class FreeUser(val fname: String, val lname: String) extends User {
 7   val i = 10
 8   def f = 1 + 2
 9 }
10 class PremiumUser(val name: String, val gender: Char, val vipnum: String) //extends User
11 
12 object ExtractorMicros {
13   implicit class UserInterpolate(sc: StringContext) {
14     object usr {
15       def apply(args: String*): Any = macro UserMacros.appl
16       def unapply(u: User): Any = macro UserMacros.uapl
17     }
18   }
19 }
20 object UserMacros {
21   def appl(c: Context)(args: c.Tree*) = {
22     import c.universe._
23     val arglist = args.toList
24     q"new FreeUser(..$arglist)"
25   }
26   def uapl(c: Context)(u: c.Tree) = {
27     import c.universe._
28     val params = u.tpe.members.collectFirst {
29       case m: MethodSymbol if m.isPrimaryConstructor => m.asMethod
30     }.get.paramLists.head.map {p => p.asTerm.name.toString}
31 
32     val (qget,qdef) = params.length match {
33       case len if len == 0 =>
34         (List(q""),List(q""))
35       case len if len == 1 =>
36         val pn = TermName(params.head)
37         (List(q"def get = u.$pn"),List(q""))
38       case  _ =>
39         val defs = List(q"def _1 = x",q"def _2 = x",q"def _3 = x",q"def _4 = x")
40         val qdefs = (params zip defs).collect {
41           case (p,d) =>
42             val q"def $mname = $mbody" = d
43             val pn = TermName(p)
44             q"def $mname = u.$pn"
45         }
46         (List(q"def get = this"),qdefs)
47     }
48 
49       q"""
50         new {
51           class Matcher(u: User) {
52             def isEmpty = false
53             ..$qget
54             ..$qdef
55           }
56           def unapply(u: User) = new Matcher(u)
57         }.unapply($u)
58       """
59   }
60 }

 

 

 

 1 name := "learn-macro"
 2 
 3 version := "1.0.1"
 4 
 5 val commonSettings = Seq(
 6   scalaVersion := "2.11.8",
 7   scalacOptions ++= Seq("-deprecation", "-feature"),
 8   libraryDependencies ++= Seq(
 9     "org.scala-lang" % "scala-reflect" % scalaVersion.value,
10     "org.scala-lang.modules" %% "scala-parser-combinators" % "1.0.1",
11     "org.specs2" %% "specs2" % "2.3.12" % "test",
12     "org.scalatest" % "scalatest_2.11" % "2.2.1" % "test"
13   ),
14   addCompilerPlugin("org.scalamacros" % "paradise" % "2.1.0" cross CrossVersion.full)
15 )
16 
17 lazy val root = (project in file(".")).aggregate(macros, demos)
18 
19 lazy val macros = project.in(file("macros")).settings(commonSettings : _*)
20 
21 lazy val demos  = project.in(file("demos")).settings(commonSettings : _*).dependsOn(macros)

macro调用当哲学原理demo目录下之HelloMacro.scala里:

然后还重新组合。具体以示范如下:

发一些值得注意的是:Macro扩展是编译中相遇方法调用时产生的,而注释目标的恢弘则当重新早同步之方法声明时。我们下又拘留注释class的例子:

首先,trait
CaseClassMapConverter[C]大凡独typeclass,代表了C类型数据的表现函数toMap和fromMap。我们还要可以看Macro定义implicit
def
Materializer[C]凡隐式的,而且是泛型的,运算结果类型是CaseClassMapConverter[C]。从者可以测算出之Macro定义通过Macro实现函数可以生出CaseClassMapConverter[C]实例,C可以是其余case
class类型。在函数ccToMap和mapTocc函数需要之隐式参数CaseClassMapConverter[C]实例就是由这Macro实现函数提供的。注意我们不得不用WeakTypeTag来赢得类参数C的信息。在用quasiquotes时我们一般是以q括号丁放入原始代码。在q括号内调用AST变量用$前缀(称为unquote)。对项目tpe的操作可以参照scala.reflect
api。示范调用代码在demo目录下的ConverterDemo.scala里:

 

 

我们盼望由此注释来进展之办法:具体做法是保存原来的代码,同时于术中前后增加几行代码。我们看看是注释的目的是安实现之:

 1 import CaseClassMapConverter._
 2 object ConvertDemo extends App {
 3 
 4   def ccToMap[C: CaseClassMapConverter](c: C): Map[String,Any] =
 5     implicitly[CaseClassMapConverter[C]].toMap(c)
 6 
 7   case class Person(name: String, age: Int)
 8   case class Car(make: String, year: Int, manu: String)
 9 
10   val civic = Car("Civic",2016,"Honda")
11   //println(ccToMap[Person](Person("john",18)))
12   //println(ccToMap[Car](civic))
13 
14   def mapTocc[C: CaseClassMapConverter](m: Map[String,Any]) =
15     implicitly[CaseClassMapConverter[C]].fromMap(m)
16 
17   val mapJohn = ccToMap[Person](Person("john",18))
18   val mapCivic = ccToMap[Car](civic)
19   println(mapTocc[Person](mapJohn))
20   println(mapTocc[Car](mapCivic))
21   
22 }
 1 object AnnotClassDemo extends App {
 2   trait Animal {
 3     val name: String
 4   }
 5   @TalkingAnimal("wangwang")
 6   case class Dog(val name: String) extends Animal
 7 
 8   @TalkingAnimal("miaomiao")
 9   case class Cat(val name: String) extends Animal
10 
11   //@TalkingAnimal("")
12   //case class Carrot(val name: String)
13   //Error:(12,2) Annotation TalkingAnimal only apply to Animal inherited! @TalingAnimal
14   Dog("Goldy").sayHello
15   Cat("Kitty").sayHello
16 
17 }
1 object HelloMacro extends App {
2   import LibraryMacros._
3   greeting("john")
4   tell("mary")
5 }

Def
Macros的Macro实现函数可以是泛型函数,支持类参数。在底下的例子我们演示如何用Def
Macros来实现通用的case
class与Map类型的更换。假设我们来只转换器CaseClassMapConverter[C],那么C类型可以是任何case
class,所以这转换器是泛型的,那么macro实现函数也就不能不是泛型的了。大体来说,我们期望实现以下功能:把另外case
class转成Map:

1 def testMethod[T]: Double = {
2     val start = System.nanoTime()
3     
4     val x = 2.0 + 2.0
5     Math.pow(x, x)
6     
7     val end = System.nanoTime()
8     println(s"elapsed time is: ${end - start}")
9   }
 1 def impl(c: Context)(annottees: c.Tree*): c.Tree = {
 2     import c.universe._
 3 
 4     annottees.head match {
 5       case q"$mods def $mname[..$tpes](...$args): $rettpe = { ..$stats }" => {
 6         q"""
 7             $mods def $mname[..$tpes](...$args): $rettpe = {
 8                val start = System.nanoTime()
 9                val result = {..$stats}
10                val end = System.nanoTime()
11                println(${mname.toString} + " elapsed time in nano second = " + (end-start).toString())
12                result
13             }
14           """
15       }
16       case _ => c.abort(c.enclosingPosition, "Incorrect method signature!")
17     }

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图