文档章节

scala动态特性与宏编程

legen_wait_dary
 legen_wait_dary
发布于 2017/04/30 16:03
字数 1421
阅读 134
收藏 0

scala动态特性

像ruby、groovy等语言都有一种动态(dynamic)的东西(如对象调用它的所述类并未创建的方法),它们属于各自语言元编程的一部分,由于它们本身就是动态弱语言,包含这些东西并不奇怪。scala是静态的强类型语言,而且所有的类型系统在编译时就要确定的,这个特性似乎比较难实现,但sip17提出了加入此功能的建议,scala团队也完成了这个,他们是通过编译器对这些代码重编码,

先直接看个例子吧:


 

class WrapperMapper[M](map: mutable.Map[String,M]) extends Dynamic{



def selectDynamic(name:String):Option[M] = map.get(name)



def updateDynamic(name:String)(value:M): Option[M] = map.put(name,value)



}



val i = new WrapperMapper[Int](mutable.Map("love" -> 2))

println(i.love)

i.hate = 5

println(i.hate)

WrapperMapper并没有love和hate属性或方法,但依然可以调用,这是为什么呢?scala的编译器在使用WrapperMapper这个类的对象的地方,如果发现它的对象调用了本不存在的方法、属性等等,就会给它加工一下,让他变成selectDynamic\updateDynamic等等方法,而本来的方法名变成相应的参数,具体的对应策略如下:

*{{{

* foo.method("blah") ~~> foo.applyDynamic("method")("blah")

* foo.method(x = "blah") ~~> foo.applyDynamicNamed("method")(("x", "blah"))

* foo.method(x = 1, 2) ~~> foo.applyDynamicNamed("method")(("x", 1), ("", 2))

* foo.field ~~> foo.selectDynamic("field")

* foo.varia = 10 ~~> foo.updateDynamic("varia")(10)

* foo.arr(10) = 13 ~~> foo.selectDynamic("arr").update(10, 13)

* foo.arr(10) ~~> foo.applyDynamic("arr")(10)

* }}}

注意,可以动态调用的类必须是继承这个特质:Dynamic,而且必须导入这个变量:scala.language.dynamics,否则编译器会编译出错(或者编译时加上一个参数:-language:dynamics)

再来看scala的宏编程(macro),这个才是scala的元编程。

大家都应该用过java的反射,scala是java的衍生语言,自然也有反射,而且它还有一种更高级的反射,就是编译时反射,它就是宏。

宏在scala是实验性产品,尚未纳入标准库中去,但不阻碍我们去使用。不过这个是有点复杂,而且ide容易报错(但实际并没有错),而且编译时有些代码需要分别编译否则会报错,所以上手很容易出错,先来看个例子:

object Debug {



def apply[T](x: => T):T = macro impl

def impl(c:blackbox.Context)(x:c.Tree) = {

import c.universe._

val q"..$stats" = x

val loggedStats = stats.flatMap {stat =>



val msg = "executing " + showCode(stat)

List(q"println(${msg})",stat)

}

q"..$loggedStats"

}



}

使用部分:

val n = Debug {

val a = 1

val b = a + 2

a + b

}

println(n)

 

debug有个apply方法,它的方法体是macro impl,也就是宏指向impl,而impl是个宏方法(我自己命名的),它接受两个参数,第一个是一个blackbox.Context,在2.10版是Context,2.11版有两个,一个是blackbox和whitebox,这两个我不花大篇幅的讲(因为我不太能讲清楚),反正这里都能用,后者用处比前者稍广一些,但scala对前者使用比较有信心,并会先将他放进标准库中。第二个参数是一个c.Tree的,它代表着原方法的参数。

再来看一个神奇的例子:

object TestImpl {

def _println[T:c.WeakTypeTag](c:blackbox.Context)(cond:c.Tree) = {

import c.universe._

// val Literal(Constant(v:Int)) = cond.tree

//这样写编译时就会打印,而不是运行时

q"""${println(cond)}"""

}

}



class PrintA[T] {

def myPrint(cond:T):Unit = macro TestImpl._println[T]

}

 

这个是我第一次测试macro写的例子,使用部分:

val printA = new PrintA[Int]

printA.myPrint(2)

当我在编译时我发现编译期间就打印了2,运行时却没有,真是太神奇了,这说明了一点,编译器它先运行了这个宏内的代码,再把q中的结果填到原来使用的该方法的地方去。q叫做“quasiquotes”,很多拥有宏编程的都有这个东东,我对宏编程不算很了解,但这里有一点我是知道的,这个quasiquotes可以取出code,而这里的${}部分会先进行计算在包含到这个code中去,scala编译器再把这个code放到调用的地方去,然后再去编译,c语言也有宏,了解它的应该知道宏可以预编译,然后在汇编时把这个预编译的代码放到调用的源码中,比如#define PI = 3.14,然后在每个用到PI的地方它做了个替换工作,而不是变量引用。scala的宏做了差不多的工作。我们吧使用部分稍作修改下:

val printA = new PrintA[Int]

val x = 2

printA.myPrint(x+2)

在编译是会打印x.+(2),是不是很有意思?这说明这个传进的c.Tree也是个表达式(其实也是个"quasiquotes")。当然这个结果不是我们想要的,所以做点修改,将_println方法改成这样子;

def _println[T:c.WeakTypeTag](c:blackbox.Context)(cond:c.Tree) = {

import c.universe._

// val Literal(Constant(v:Int)) = cond.tree

//这样写编译时就会打印,而不是运行时

// q"""${println(cond)}"""

//这样才起作用

q"""println($cond)"""

}

现在在来分析第一个例子,首先通过解构,拿到表达式的每一条语句(都是一个tree),然后通过flatMap将一条语句变成两条,第一条是打印语句表达式,第二个就是原语句不变,所以在使用部分的代码变成如下形式:

println("val a = 1")

val a = 1

println("val b = a.+(2)")

val b = a + 2

println("a.+(b)")

a + b

scala的宏我能介绍的差不多就这些了,还有一些特性,如宏注解、类型提供者等等,我也在研究中,并思考其用途。

© 著作权归作者所有

共有 人打赏支持
legen_wait_dary
粉丝 0
博文 10
码字总数 6658
作品 0
荆州
私信 提问
编程语言--Scala

Scala是一门现代的多范式编程语言,志在以简练、优雅及类型安全的方式来表达常用编程模式。它平滑地集成了面向对象和函数语言的特性。 Scala是面向对象的:Scala是一个纯面向对象语言,在某种...

匿名
2008/11/11
71.1K
12
编程语言 Scala 2.12.0 正式版发布

编程语言 Scala 2.12.0 正式版发布了。 Scala 2.12.0编译器已经完全翻修,在Java 8中提供的新VM功能: trait使用默认方法直接编译到接口,这提高了二进制兼容性和Java互操作性。 Scala和Jav...

达尔文
2016/11/04
3.7K
15
编程语言 Scala 2.12.0-M1 发布

Scala 2.12.0-M1 发布,Scala 2.12 系列的目标是 Java 8。 下载:scala-lang.org Maven Central Scala 2.12 改进计划: Java 8 style closures. Lambda syntax for SAM types New backend a......

oschina
2015/05/10
2.6K
16
Scala 2.11.2 发布!

Scala 2.11.2 发布,此版本现已提供下载,也可以通过 Maven 库获取。此版本是个 bug 修复版本,二进制兼容 Scala 2.11 系列,改进记录: Several issues in the collections library were r...

oschina
2014/07/27
2.7K
1
编程语言 Scala 2.11.7 发布

编程语言 Scala 2.11.7 发布,值得关注的更新如下: The Scala REPL is now easier for projects such as Spark to embed and customize (PR #4563, SI-7747). Pattern matching is now fast......

oschina
2015/06/24
4.3K
24

没有更多内容

加载失败,请刷新页面

加载更多

一致性hash和虚拟节点

consistent hashing 算法的原理 consistent hashing 是一种 hash 算法,简单的说,在移除 / 添加一个 cache 时,它能够尽可能小的改变已存在key 映射关系,尽可能的满足单调性的要求。 下面就...

群星纪元
12分钟前
0
0
说一下Dubbo 的工作原理?注册中心挂了可以继续通信吗?

面试题 说一下的 dubbo 的工作原理?注册中心挂了可以继续通信吗?说说一次 rpc 请求的流程? 面试官心理分析 MQ、ES、Redis、Dubbo,上来先问你一些思考性的问题、原理,比如 kafka 高可用架...

李红欧巴
23分钟前
12
0
腾讯面试:一条SQL语句执行得很慢的原因有哪些?

说实话,这个问题可以涉及到 MySQL 的很多核心知识,可以扯出一大堆,就像要考你计算机网络的知识时,问你“输入URL回车之后,究竟发生了什么”一样,看看你能说出多少了。 之前腾讯面试的实...

java菜分享
41分钟前
10
0
Java 基本功 之 CAS

本文首发于个人公众号《andyqian》, 期待你的关注! 前言 在Java并发编程中,我们经常使用锁对竞争资源予以并发控制,以解决资源竞争的问题。但无论是使用 Lock 还是 Synchronized,随着锁机...

andyqian
45分钟前
4
0
信号量与条件变量的区别

注意信号量与条件变量的区别 信号量内容可见:http://www.cnblogs.com/charlesblc/p/6142868.html 信号量、共享内存,以及消息队列等System V IPC三剑客主要关注进程间通信; 而条件变量、互...

shzwork
56分钟前
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部