神奇的Scala Macro之旅(二)- 一个实例
优化的日志方式
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的编写可以进一步的降低吧。
参考:
神奇的Scala Macro之旅(一)- 什么时候用宏
转自:神奇的Scala Macro之旅(2)
神奇的Scala Macro之旅(二)- 一个实例的更多相关文章
- 神奇的Scala Macro之旅(三)- 实际应用
在上一篇中,我们示范了使用macro来重写 Log 的 debug/info 方法,并大致的介绍了 macro 的基本语法.基本使用方法.以及macro背后的一些概念, 如AST等.那么,本篇中,我们 ...
- 神奇的Scala Macro之旅(一)- 什么时候用宏
在Lisp语言中,macro是一个神器,可以“动态的生成代码”,然后被执行,这种方式给到Lisp无限的表达能力.除Lisp之外,很少有语言支持Macro这个特性,我记得 GWT之中曾经有一个类似的Ge ...
- 神奇的Scala Macro之旅(四)- BeanBuilder
在Java开发中,经常会有一个需求,将一个 Bean 复制到另外一个 Bean,尤其是在后台分层的场景下,在不同的层之间传递信息,经常需要进行 这样的一个对象复制工作,类似于: val source: ...
- WCF学习之旅——第一个WCF示例(二)
第四步:通过自我寄宿的方式寄宿服务 WCF服务需要依存一个运行着的进程(宿主),服务寄宿就是为服务指定一个宿主的过程.WCF是一个基于消息的通信框架,采用基于终结点(Endpoint)的通信手段. 终 ...
- WCF学习之旅——第一个WCF示例(一)
最近需要用到WCF,所以对WCF进行了解.在实践中学习新知识是最快的,接下来先做了一个简单的WCF服用应用示例. 本文的WCF服务应用功能很简单,却涵盖了一个完整WCF应用的基本结构.希望本文能对那些 ...
- WCF学习之旅——第一个WCF示例(三)
第五步:创建客户端 WCF应用服务被成功寄宿后,WCF服务应用便开始了服务调用请求的监听工作.此外,服务寄宿将服务描述通过元数据的形式发布出来,相应的客户端就可以获取这些元数据.接下来我们来创建客户端 ...
- scala函数式编程(二) scala基础语法介绍
上次我们介绍了函数式编程的好处,并使用scala写了一个小小的例子帮助大家理解,从这里开始我将真正开始介绍scala编程的一些内容. 这里会先重点介绍scala的一些语法.当然,这里是假设你有一些ja ...
- Scala学习笔记(二)表达式和函数
笔记的整理主要针对Scala对比Java的新特性: 1.if表达式 if表达式是有结果返回的. val a= if (5>2) "你好" else 1 a的值为if表达式 ...
- Akka.NET是Java/Scala 流行框架Akka的一个 .NET 开源移植
Akka.NET v1.0 已发布,支持Mono Akka.NET 是Java/Scala 流行框架Akka的一个 .NET 开源移植.可用于构建高并发,分布式和容错事件驱动的应用在 .NET 和 M ...
随机推荐
- python爬虫——词云分析最热门电影《后来的我们》
1 模块库使用说明 1.1 requests库 requests 是用Python语言编写,基于 urllib,采用 Apache2 Licensed 开源协议的 HTTP 库.它比 urllib 更 ...
- 分布式缓存管理平台XXL-CACHE
<分布式缓存管理平台XXL-CACHE> 一.简介 1.1 概述 XXL-CACHE是一个分布式缓存管理平台,其核心设计目标是"让分布式缓存的接入和管理的更加的简洁和高效&quo ...
- Thymeleaf中each标签遍历list如何获取index
<tr th:each="user,userStat:${users}">userStat是状态变量,有 index,count,size,current,even,o ...
- Masonry 抗压缩 抗拉伸
约束优先级: 在Autolayout中每个约束都有一个优先级, 优先级的范围是1 ~ 1000.创建一个约束,默认的优先级是最高的1000 Content Hugging Priority: 该优先级 ...
- WBS 与 甘特图
WBS:工作分解结构(Work Breakdown Structure) 创建WBS:创建WBS是把项目 交付成果和项目工作分解成较小的,更易于管理的组成部分的过程. WBS是项目管理重要的专业术语之 ...
- Myeclipse10破解版安装包
下载地址;http://pan.baidu.com/s/1pLka0un
- 【java错误】Non-terminating decimal expansion; no exact representable decimal result
问题描述 意思是“无法结束的除法表达式:没有精确的除结果”.当时输入的10/3,结果应该是3.3333....333. 下面这种处理方式有问题. BigDecimal num3 = new BigDe ...
- 渐进式Web应用(PWA)入门教程(上)
最近关于渐进式Web应用有好多讨论,有一些人还在质疑渐进式Web应用是否就是移动端未来. 但在这篇文章中我并不会将渐进式APP和原生的APP进行比较,但有一点是可以肯定的,这两种APP的目标都是使用户 ...
- 【SpringMVC】从Fastjson迁移到Jackson,以及对技术选型的反思
为什么要换掉fastjson 直接原因是fastjson无法支持注解形式的自定义序列化和反序列化,虽然其Github上的Wiki上说明是支持的.但是实测结果表明:Test类的序列化被fastjson的 ...
- WARN: Establishing SSL connection without server's identity verification is not recommended
0.要想用Java连接mysql数据库,首先装好JDK,配置好环境变量,将jdk*.*.*\lib放入classpath,将jdk*.*.*\bin放入path中(*.*.*表示版本号):其次安装好m ...