『阿男的编程本质论』*08 Eval,Macro,Preprocessor,Homoiconicity(一)*

『阿男的编程本质论』*08 Eval,Macro,Preprocessor,Homoiconicity(一)*

阿男这次想跟大家聊聊语言设计里面的几个概念,分别是Eval,Macro,Preprocessor和Homoiconicity。

首先是Eval,我们在很多语言里都可以看到它的影子,比如PythonPerlRuby还有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-Time Compiler。有了这个能力,就允许代码在运行时被实时地编译,然后虚拟机里面的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是主流趋势。

转载于:https://my.oschina.net/u/3195023/blog/822775

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值