『阿男的编程本质论』*08 Eval,Macro,Preprocessor,Homoiconicity(一)*
2017-01-11 00:00
155 查看
『阿男的编程本质论』*08 Eval,Macro,Preprocessor,Homoiconicity(一)*
阿男这次想跟大家聊聊语言设计里面的几个概念,分别是Eval,Macro,Preprocessor和Homoiconicity。
首先是
从上面的例子我们可以看到,
Perl和Python还有Javascript都是差不多的使用方法。我们可以想一想,似乎Java语言,C语言里面并没有
我们得想一想
C语言或者Java语言这种,是明确区分Compile Phase和Runtime Phase的。我们的C语言代码,需要被编译器先编译成汇编代码,变成一个可执行文件的。而我们使用的,是编译后的可执行文件,因此我们不可能让编译后的代码,去解析一个字符串,再把它变成汇编代码。
Java的编译过程也是和运行时互相独立的,需要把Java文件编译成class文件,class文件里面有bytecode,在JVM上面执行。
当然,Java发展到现在,也支持Compile-at-runtime,这种技术有个名字,叫做
但是光有Compile-at-runtime还不够,我们还得看这门语言自身的语言特性是否支持runtime时代码自身的改变。Java在设计的时候,对class之间的依赖关系,package之间的关系等等,都有很多约定,可以说Java是一门设计的比较 "严格"的语言,所以实现
因此,现在有很多语言,是架构在JVM平台上,最终也是compile成bytecode,但是设计的比Java语言更灵活一些,因此就可以实现
从上面的例子,我们可以看到Groovy的
Ruby也有自己的虚拟机和virtual machine code叫做YARV,阿男之前给大家讲过了。因为Ruby也支持Compile-at-runtime,所以实现
Python,Perl和Javascript也是一样,所以我们要明白的是,如果一门语言在语言设计和虚拟机设计上支持Compile-at-runtime,那么这门语言就可以在运行时,把一串字符编译成代码并且加载执行,也就是实现了
这种功能固然很方便很强大,但是它有什么问题没有?
其实语言设计里面,并不存在绝对的优势,有的功能,它的优点同时也就是它的缺点,比如
因此Compile-at-runtime虽然很灵活,但是compile和runtime混在一起的话,也就意味着很多代码的问题会在运行时才能发现。而C语言或者Java,很多问题会在明确的compile这一步被发现,而不是runtime时才被发现。
最后我们要说一下,以上的讨论都是基于这一门语言有compile这一步骤而讨论的,其实还有更简单的实现,就是Interpreter。所谓Interpreter,就是指直接解析文本,然后根据文本直接执行命令,并没有把源代码转化成目标代码的过程,也没有用来运行目标代码的虚拟机。比如早期的Ruby就是使用的这种形式,提供一堆用C语言编写的API接口,然后Ruby代码就直接解析,然后调用这些接口来执行代码。
Interpreter的执行效率和功能上面都有局限,所以Ruby从1.9开始就转为使用虚拟机加自己的虚拟机YARV code的形式来执行代码,这一点和JVM平台越来越像。可以说,VM加VM code是主流趋势。
阿男这次想跟大家聊聊语言设计里面的几个概念,分别是Eval,Macro,Preprocessor和Homoiconicity。
首先是
Eval,我们在很多语言里都可以看到它的影子,比如
Python,
Perl,
Ruby还有
Javascript,等等。以Ruby为例,下面是在Ruby里面使用
eval的例子:
irb(main):001:0> eval "1+1" => 2
从上面的例子我们可以看到,
eval的用处就是接受一段字符串作为代码,并且执行这个代码。就像上面这样,我们的字符串是
1+1,使用
eval作用在这个字符串上面,它就作为了Ruby里面的代码来执行了,然后给出结果为
2。
Perl和Python还有Javascript都是差不多的使用方法。我们可以想一想,似乎Java语言,C语言里面并没有
eval这种功能,为什么?
我们得想一想
eval是怎么实现的,把字符串作为代码,等于就是在程序Runtime的时候,这个字符串可以「编译」成代码。因此,也就是说这门语言需要具备Compile-at-runtime的能力。
C语言或者Java语言这种,是明确区分Compile Phase和Runtime Phase的。我们的C语言代码,需要被编译器先编译成汇编代码,变成一个可执行文件的。而我们使用的,是编译后的可执行文件,因此我们不可能让编译后的代码,去解析一个字符串,再把它变成汇编代码。
Java的编译过程也是和运行时互相独立的,需要把Java文件编译成class文件,class文件里面有bytecode,在JVM上面执行。
当然,Java发展到现在,也支持Compile-at-runtime,这种技术有个名字,叫做
JIT,也就是
Just-In-TimeCompiler。有了这个能力,就允许代码在运行时被实时地编译,然后虚拟机里面的class文件在运行时被实时地加载。
但是光有Compile-at-runtime还不够,我们还得看这门语言自身的语言特性是否支持runtime时代码自身的改变。Java在设计的时候,对class之间的依赖关系,package之间的关系等等,都有很多约定,可以说Java是一门设计的比较 "严格"的语言,所以实现
eval是有难度的。
因此,现在有很多语言,是架构在JVM平台上,最终也是compile成bytecode,但是设计的比Java语言更灵活一些,因此就可以实现
eval的功能,比如Groovy,还有后续要讲到的Clojure。我们可以看看Groovy的eval的代码例子:
$ groovysh Groovy Shell (2.4.7, JVM: 1.8.0_112) Type ':help' or ':h' for help. ------------------------------------------------------------------------------- groovy:000> Eval.me('2 * 4 + 2') ===> 10
从上面的例子,我们可以看到Groovy的
Eval的使用方法。接下来我们看Ruby的
eval:
$ irb
irb(main):001:0> eval "1+1" => 2
Ruby也有自己的虚拟机和virtual machine code叫做YARV,阿男之前给大家讲过了。因为Ruby也支持Compile-at-runtime,所以实现
eval是没问题的。
Python,Perl和Javascript也是一样,所以我们要明白的是,如果一门语言在语言设计和虚拟机设计上支持Compile-at-runtime,那么这门语言就可以在运行时,把一串字符编译成代码并且加载执行,也就是实现了
eval的功能。
这种功能固然很方便很强大,但是它有什么问题没有?
其实语言设计里面,并不存在绝对的优势,有的功能,它的优点同时也就是它的缺点,比如
eval这种功能就是一个典型的例子。因为我们可以把代码封装在字符串里,然后在运行时编译并执行,那么如果这个字符串里面的代码有问题,这个问题会在运行时才发现。
因此Compile-at-runtime虽然很灵活,但是compile和runtime混在一起的话,也就意味着很多代码的问题会在运行时才能发现。而C语言或者Java,很多问题会在明确的compile这一步被发现,而不是runtime时才被发现。
最后我们要说一下,以上的讨论都是基于这一门语言有compile这一步骤而讨论的,其实还有更简单的实现,就是Interpreter。所谓Interpreter,就是指直接解析文本,然后根据文本直接执行命令,并没有把源代码转化成目标代码的过程,也没有用来运行目标代码的虚拟机。比如早期的Ruby就是使用的这种形式,提供一堆用C语言编写的API接口,然后Ruby代码就直接解析,然后调用这些接口来执行代码。
Interpreter的执行效率和功能上面都有局限,所以Ruby从1.9开始就转为使用虚拟机加自己的虚拟机YARV code的形式来执行代码,这一点和JVM平台越来越像。可以说,VM加VM code是主流趋势。
相关文章推荐
- 『阿男的编程本质论』*09 Eval,Macro,Preprocessor,Homoiconicity(二)*
- 『阿男的编程本质论』*10 Eval,Macro,Preprocessor,Homoiconicity(三)*
- 『阿男的编程本质论』*11 Eval,Macro,Preprocessor,Homoiconicity(四)*
- 『阿男的编程本质论』*08 正则表达式是万能的吗?*
- Homoiconicity
- 术语:Homoiconicity(同像)
- Java线程中断的本质和编程原则
- 乱弹:论编程的本质
- 编程的本质
- 编程的本质
- 编程思想的本质
- 编程思想与以人为本-编程的本质
- 编程本质论
- Java面向对象设计编程思想(Essential Object Oriented Software Construction)的本质----百家争鸣 陈光剑
- 浅入深谈:如何更好地理解面向对象编程与面向过程编程的本质区别?
- 写给初学者:编程的本质
- 附录B 编程的本质
- 阻塞/非阻塞读写总结、tcp网络编程的本质、muduo::Buffer设计简介
- 深入Java线程中断的本质与编程原则的概述
- 探讨Windows编程中句柄的本质