博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
神奇的Scala Macro之旅(二)- 一个实例
阅读量:5326 次
发布时间:2019-06-14

本文共 2827 字,大约阅读时间需要 9 分钟。

优化的日志方式

package macros_demo import scala.language.experimental.macros import org.slf4j._ import scala.reflect.macros.whitebox.Context object Macros {     implicit class LoggerEx(val logger: Logger) {         def DEBUG(msg: String): Unit = macro LogMacros.DEBUG1    def DEBUG(msg: String, exception: Exception): Unit = macro LogMacros.DEBUG2  }     object LogMacros {         def DEBUG1(c: Context)(msg: c.Tree): c.Tree = {             import c.universe._             val pre = c.prefix      q"""         val x = $pre.logger         if( x.isDebugEnabled ) x.debug($msg)       """    }         def DEBUG2(c:Context)(msg: c.Tree, exception: c.Tree): c.Tree = {             import c.universe._             val pre = c.prefix      q"""         val x = $pre.logger         if(x.isDebugEnabled) x.debug( $msg, $exception )       """    }  } } package macros_test import org.slf4j._ import macros_demo.Macros._ class LogTest {     val logger = LoggerFactory.getLogger(getClass)  logger.DEBUG(s"Hello, today is ${new java.util.Date}") }

在这个例子中:

  • 我们通过隐式转换的方式,为 org.slf4j.Logger 扩展了 DEBUG 方法,使用上与 原有的debug 一致,我们期望新的 DEBUG 匹配如下的模式:

// logger.DEBUG(message) will expand to at compile timeif(logger.isDebugEnabled) logger.debug(message)
  • 可以使用这个选项来看看 scala 编译生成的代码:(可以直接在sbt中 set scalacOption := Seq(“-Ymacro-debug-lite”)开启选项)

 val x = macros_demo.Macros.LoggerEx(LogTest.this.logger).logger;     if (x.isDebugEnabled)    x.debug(scala.StringContext.apply("Hello, today is ", "").s(new java.util.Date()))     else    ()    //Block(List(ValDef(Modifiers(), TermName("x"), TypeTree(), Select(Apply(Select(Select(Ident(macros_demo), macros_demo.Macros), TermName("LoggerEx")), List(Select(This(TypeName("LogTest")), TermName("logger")))), TermName("logger")))), If(Select(Ident(TermName("x")), TermName("isDebugEnabled")), Apply(Select(Ident(TermName("x")), TermName("debug")), List(Apply(Select(Apply(Select(Select(Ident(scala), scala.StringContext), TermName("apply")), List(Literal(Constant("Hello, today is ")), Literal(Constant("")))), TermName("s")), List(Apply(Select(New(Select(Select(Ident(java), java.util), java.util.Date)), termNames.CONSTRUCTOR), List()))))), Literal(Constant(()))))

 

上面的第一段代码,是 scalac 生成的等效代码,可以看到,已经符合了我们的预期,尽在debug级别生效时,才会对messgae进行求助计算,避免不必要的开销,使得这段代码,在debug级别关闭时,基本上没有任何性能的损失。 

 

而第二段代码,有如天书,难以阅读。其实,这就是scalac内部的对这一段代码的表示格式,一般的,我们称之为 Abstracted Syntax Tree(AST),有兴趣的同学,可以通过这个网站 *AST explorer* 来帮助阅读AST。 scala 2.10时代,写macro,就必须自己来构建AST,相当于你要徒手写出这么复杂的一个表达式,这是一件近乎不可完成的任务,所以,macro书写的难度时及其至高的,好在后续的版本中提供了 q”” 插值,我们可以直接使用q”val x = $pre.logger; if( x.isDebugEnabled ) x.debug($msg)”来替代上面这么一个复杂的AST,让 macro 的编写门槛极大幅度的降低下来。

 

不过,即使这样,要想很好的驾驭macro,你还是要懂一些 AST 的知识,否则,还是很难的。 所以,书写Macro,其实就是一个和编译器协同工作的过程,这就是macro的难度之所在。或许,未来,随着 scalameta 和 dotty的成熟,macro的编写可以进一步的降低吧。

 

参考: 

转自:

转载于:https://www.cnblogs.com/barrywxx/p/10779401.html

你可能感兴趣的文章
C语言学习总结(三) 复杂类型
查看>>
HNOI2018
查看>>
【理财】关于理财的网站
查看>>
Ubunt中文乱码
查看>>
《当幸福来敲门》读后
查看>>
【转】系统无法进入睡眠模式解决办法
查看>>
省市县,循环组装,整合大数组
查看>>
stm32中字节对齐问题(__align(n),__packed用法)
查看>>
like tp
查看>>
posix多线程有感--线程高级编程(线程属性函数总结)(代码)
查看>>
spring-使用MyEcilpse创建demo
查看>>
DCDC(4.5V to 23V -3.3V)
查看>>
kettle导数到user_用于left join_20160928
查看>>
activity 保存数据
查看>>
typescript深copy和浅copy
查看>>
linux下的静态库与动态库详解
查看>>
hbuilder调底层运用,多张图片上传
查看>>
深入理解基于selenium的二次开发
查看>>
较快的maven的settings.xml文件
查看>>
Git之初体验 持续更新
查看>>