语言自由度在不同的领域有不同的定义。我们指的是用数学中构成空间的维度来表示其自由度的方法。这里指的是:解决同一个问题的互不相关的设计方法论的数量。比如解决一个商品打折这样的问题,下单和提取函数怎么设计,具体思路可能有很多,但这可能是从面向过程(OP)的角度来解决这个问题,如果换一种语言支持面向对象(OO)的设计方法,那么我们认为后者的自由度更大,因为OO提供了几乎完全从另一个角度解决问题的能力。既然自由度可以参考“维度”的定义,那我们就试着分析一下计算机语言的“维度”。在此之前,我们需要简单分析一下语言是如何一步步变得复杂的。本文重点介绍命令式计算机语言。第一步是结构体(数据结构)、常量、变量、运算符、序列、分支、循环等的出现,体现了“命令”的基本方面;第二步是例程的外观,包括函数、程序等;第三步是宏的出现,包括宏、模板、泛型;第四步是出现对客观世界的结构化抽象能力,包括面向对象;第五步是元编程能力的出现,比如注解、反射等等;……从计算机语言的历史来看,以上步骤不一定按时间顺序进行。我们更关心语言能力提升带来的意义。其中,第二步的完成标志着结构化编程方法的出现,为大型软件项目提供了更好的支持。第三步是对第二步的进一步抽象,而第四步则代表了更显着的意义,这很重要,意味着它最终可以支持实现“层级”,可以实现“核心”和“外围”的分离,分离出相对稳定和潜在变化的部分,即内容由代码表达,不再只能扁平化,最终演化出一个“类”。从本质上讲,上述演变反映了语言本身抽象能力的不断提升。这里一个很有意思的现象是,抽象的不断提升会把语言的维数增加到一定的分形维数——抽象的本质是在空间上提供一个自相似的递归映射,从而体现出“分形”的分形结构展示即在原有空间上增加了分形维数,但很难得到新的整数维,即1维可以提升到1.5维,但达不到2维。因此,大多数高级计算机语言的维数都是1.X。但是第五步意味着语言开始真正走向更高的维度。实际上,元编程的实现方式有很多种。从语言本身来看,可以分为两类:增强的API和新的语法实现。前者用反射来表示,后者用注解来表示。我们来看一个例子:publicclassTestCase{@BeforepublicvoidsetUp()throwsException{}@AfterpublicvoidtearDown()throwsException{}@Testpublicvoidadd(){}}上面就是使用的Annotation类型Java语言定义了一个单元测试的三个阶段,这里是:@Before,@After,@Test用“变量”定义了“变量”,并定义了执行顺序,这里是“编码和编码”的过程,就是元编程的典型实现。当然我们也可以通过增强的API(反射或者设计约束(比如模板方法))来解决,但是无论是哪一种,都没有Annotation那么简单直接。根本原因在于增强API的实现与原代码中表达逻辑的两个维度,即1.X维度存在太多“相关性”,但Annotation的方式在条款上大大减少的相关性。第一个维度的解耦程度更高,所以后者的自由度更高。以下基于Mocha的JS单元测试代码:describe('testprocess1',function(){it('1+1',function(){expect(fn_add(1,1)).to.be.equal(2);});});我们期待这样的编程风格:'@test(step=testprocess1,name=1+1,expect=2';varstep0=function(){returnfn_add(1,1);}JS实现基于注解的元编程我们尝试在JS中引入Annotation机制,如下:'@Log(level=info,dateFormat=YYYY-MM-DDHH:mm)';varlogInfo=function(_msg){console.log(_msg);}为复杂场景,考虑多个注解的依赖:'@Start';varserverStart=function(){}'@Rule(fileType=.(html|htm))';varproHtml=function(_req,_res){}'@Rule(fileType=.(jpg|gif|webp))';varproPic=function(_req,_res){}'@Finish';varserverFinish=function(){}At-js框架就是基于上面的思路。我们实现了At-js框架,并开源了。At-js的实现思路很简单。在Node上。对于Enhance处理,出于性能考虑,At-js采用了正则扫描而不是AST。在-js的使用方法包括:定义注解和使用注解。定义注解:require('at-js').define('helloworld',{//注解名称scope:'var',build:function(){//作用域return"returnfunction(_msg){console.log('[helloworld]'+_msg);};"//真正的脚本}})使用注释:'@helloworld';varsayHello=function(){}sayHello('here')运行效果:[Helloworld]here下面的代码描述了一个单元测试过程(https://github.com/CheMingjun...):'@test.start';varstart=function(){ds={};}'@test.步骤(超时=2000)';vartest0=function*(){ds.test0='finish';varrtn=yield(function(){returnfunction(_next){setTimeout(function(){_next(null,3);},2000)}})();assert.equal(rtn,3);}'@test.step';vartest1=function(){ds.test1='finish';returnds;}'@test.finish';varfh=function(){ds=null;}At-js支持Var级别和File级别的注解定义。上面的例子属于File级别的复杂注解定义。两者的API如下:VartypeannotationDefinition:{scope:'var',build:function(_ctx,_argAry){//_ctx{filePath,//需要注释的文件位置名,//注释名desc,//注释中的变量表(key-value)refName,//注释的变量名refType//注释的变量类型(undefined|function|generator|object)}//_aryAry是注解变量签名中的参数列表return//返回变量被替换后的代码}}文件类型注解定义:{return{which:{//用于重组注解每个项目都处理'test.start':function(_ctx,_argAry){//_ctx和_argAry定义同上//处理逻辑}},script:function(){return//返回附加到这个文件的代码}}}在实际生产过程中,如下一组注解实现了ORM:'@dao.column';变量标识;'@dao.column(名字=名字)';变量名称;'@dao.column(name=status)';变量状态;'@dao.column(name=creator_id)';变种创造者;'@dao.column(name=creator_name)';var创作者姓名;'@dao.column(name=gmt_create)';varcreateTime=函数(_time){varmm=require('时刻');返回mm(_time).format("YYYY-MM-DDHH:mm:ss");}'@dao.column(name=gmt_update)';varupdateTime=function(_time){varmm=require('moment');返回mm(_time).format("YYYY-MM-DDHH:mm:ss");}'@dao.column(name=type)';vartype;Summary本文给出了语言自由度的简单定义,并在此基础上讨论了语言发展过程中呈现出的不同复杂性,探讨了元编程如何从根本上提高语言的自由度。本部分我们尝试在JS语言的基础上加入原生的元编程能力,并介绍了这一思想的实现:At-js框架。
