语言的自由度

自由度这个概念在不同领域有不同的定义,我们借鉴数学中构成一个空间的维数来表达其自由度的做法,在此指的是:解决同一个问题彼此不相关的设计方法学数量。

例如,解决一个比如商品打折的问题,如何设计顺序、提取函数,具体的思路可能有很多,但是这可能都是从面向过程(OP)的角度,同样解决这个问题,如果另一门语言还支持面向对象(OO)的设计方法,那么我们认为后者的自由度要多一些,因为OO提供了几乎完全从另一个角度解决问题的能力。

既然自由度可以借鉴“维数”的定义,我们来尝试分析一下计算机语言的“维数”,在此之前,我们有必要简单分析一下语言是怎样一步一步变得复杂的。

本文关注的重点是命令式风格的计算机语言。

第一步,出现了结构体(数据结构)、常量、变量、算符、顺序、分支、循环等这些体现“命令”的基本方面;

第二步,例程的出现,包括函数、过程等;

第三步,宏的出现,包括宏、模板、泛型;

第四步,对客观世界在结构化上抽象能力出现,包括OO等;

第五步,元编程能力的出现,如注释、反射等等;

从计算机语言历史来看,以上步骤不一定按照时间顺序展开,我们更关注的是语言能力提升带来的意义。其中,第二步的完成,标志着结构化程序设计方法的出现,对大型软件工程提供了较好的支持,第三步是对第二步的进一步抽象,第四步所代表的意义更加重大,其中非常重要的一点,意味着终于可以支持实现“层次化”,可以实现将“内核”与“外围”做分离,将相对稳定与潜在变化的部分分开,也就是说,编码所表达的内容不再只能扁平化,终于进化出了“阶级”。

从本质上来讲,以上演进反映了语言自身抽象能力的不断提升。

这里非常有意思的一个现象是,抽象化的不断提升,会使得语言的维度提升至某个分数维——抽象的本质是在空间上提供了某种自相似的递归映射,从而体现出“分形”的结构形式,分形结构表现出在原有空间中增加了分数维,但是得到一个新的整数维是困难的,即比如1维可以提升至1.5维,但是无法到达2维。

所以,目前绝大部分计算机高级语言的维度是1.X。

但是第五步,意味着语言开始真正走向一个更高的维度。

事实上,实现元编程有多种方式,从语言本身来讲,可以分为两类:增强型API与新的语法实现,前者的代表是反射,后者的代表为Annotation。

我们来看一个例子:

public class TestCase{
@Before
public void setUp() throws Exception{}
@After
public void tearDown() throws Exception{}
@Test
public void add() {}
}

上面是Java语言中使用Annotation类型定义了一个单元测试的三个阶段,在这里:
@Before、@After、@Test用“变量”定义了“变量”,同时定义了执行的顺序,这里是“对编码再进行编码”的过程,是元编程的一种典型的实现。

我们当然也可以通过增强型API(反射或者用设计约束(比如摸版方法))来解决,但是无论哪一种,都不如Annotation的方式要简单直接明了。

根本的原因,在于增强型API的实现方式与原有代码这两个表达逻辑的维度存在过多的“相关性”,即1.X维的,但Annotation的方式在相关性上大大减少,两个维度更加解耦,所以后者的自由度更高。

如下JS基于Mocha的单元测试代码:

describe('测试过程1', function() {
it('1+1', function() {
expect(fn_add(1, 1)).to.be.equal(2);
});
});

我们期望如下编程风格:

'@test(step=测试过程1,name=1+1,expect=2';
var step0 = function(){
return fn_add(1, 1);
}

JS实现基于注释的元编程

我们尝试将Annotation的机制引入JS,如下:

'@Log(level=info,dateFormat=YYYY-MM-DD HH:mm)’;
var logInfo = function(_msg){
console.log(_msg);
}

复杂的场景,考虑多个注释的相关性:

'@Start';
var serverStart = function(){} '@Rule(fileType=.(html|htm))';
var proHtml = function(_req,_res){} '@Rule(fileType=.(jpg|gif|webp))';
var proPic = function(_req,_res){} '@Finish';
var serverFinish = function(){}

At-js框架

基于以上想法,我们实现了At-js框架并开源,At-js的实现思路非常简单,在Node.js端,通过覆盖运行时JS文件加载机制实现对Annotation类型的识别判断并对原生文件进行Enhance处理,为性能考虑,At-js采用了正则扫描而非AST的方式。

At-js使用方法包括:定义注释与使用注释。

定义注释:

require('at-js').define('helloworld',{//annotation's name
scope: 'var', build: function () {//the scope of it's effected
return "return function(_msg) {console.log('[helloworld]'+_msg);};"//the real script
}
})

使用注释:

'@helloworld';
var sayHello = function(){} sayHello('here')

运行效果:

[Helloworld]here

以下代码描述了一个单元测试过程(https://github.com/CheMingjun...):

'@test.start';
var start = function () {
ds = {};
} '@test.step(timeout=2000)';
var test0 = function* () {
ds.test0 = 'finish';
var rtn = yield (function(){
return function(_next){
setTimeout(function(){
_next(null,3);
},2000)
}
})();
assert.equal(rtn,3);
} '@test.step';
var test1 = function () {
ds.test1 = 'finish';
return ds;
} '@test.finish';
var fh = function () {
ds = null;
}

At-js支持Var级及File不同级别的注释定义,上例属于File级别复杂的注释定义,两者的API如下:

Var型注释定义:

    {
scope:'var',
build:function(_ctx, _argAry){
//_ctx
{
filePath,//应该该注释的文件位置
name,//注释名称
desc,//注释中的变量表(key-value)
refName,//被注释的变量名称
refType//被注释的变量类型(undefined|function|generator|object)
}
//_aryAry 被注释变量签名中的参数列表 return //返回该变量被替换之后的代码
}
}

File型注释定义:

    {
return {
which: {//针对改组annotation中的每一项做处理
'test.start': function (_ctx, _argAry) {
//_ctx 与 _argAry 同上定义
//处理逻辑
}
}, script: function () {
return //返回该文件追加的代码
}
}
}

在实际生产过程中,如下一套注释实现了ORM:

    '@dao.column';
var id; '@dao.column(name=name)';
var name; '@dao.column(name=status)';
var status; '@dao.column(name=creator_id)';
var creatorId; '@dao.column(name=creator_name)';
var creatorName; '@dao.column(name=gmt_create)';
var createTime = function (_time) {
var mm = require('moment');
return mm(_time).format("YYYY-MM-DD HH:mm:ss");
} '@dao.column(name=gmt_update)';
var updateTime = function (_time) {
var mm = require('moment');
return mm(_time).format("YYYY-MM-DD HH:mm:ss");
} '@dao.column(name=type)';
var type;

总结

本文给出了语言自由度的简单定义,并在此基础上论述了在语言发展过程中呈现的不同复杂性,并探讨了元编程是如何从根本上增加语言的自由度的。在第二部分,我们尝试在JS语言基础上增加原生的元编程能力并介绍了该思路的实现:At-js框架。

Javascript元编程之Annotation的更多相关文章

  1. javascript 元编程之 method_missing

    javascript 元编程之 method_missing 引言 要说元编程 ruby 中技巧太多了,今天来写的这个技术也来自于 ruby 中的灵感. method_missing 这个在 ruby ...

  2. Javascript异步编程之setTimeout与setInterval详解分析(一)

    Javascript异步编程之setTimeout与setInterval 在谈到异步编程时,本人最主要会从以下三个方面来总结异步编程(注意:特别解释:是总结,本人也是菜鸟,所以总结不好的,请各位大牛 ...

  3. 【转】Javascript异步编程之setTimeout与setInterval

    Javascript异步编程之setTimeout与setInterval 转自:http://www.tuicool.com/articles/Ebueua 在谈到异步编程时,本人最主要会从以下三个 ...

  4. JavaScript模块化编程之AMD - requireJS基础使用

    JavaScript模块化编程之AMD requireJS基础使用 标签(空格分隔): JavaScript 参考文章 AMD规范 AMD是"Asynchronous Module Defi ...

  5. JavaScript模块化编程之require.js与sea.js

    为什么要模块化:当今,网站以不再是一个简单的页面,JavaScript也不再是做一些简单的脚本验证,随着WEB.20时代到来,前端工程师面临的必将是越来越庞大的JavaScript代码,越来越复杂的内 ...

  6. 我也来谈javascript高级编程之:javascript函数编译过程

    前言 题目有点大,其实也就是手痒...跟大家来扯一下javascript编译过程. 那么到底什么是“编译”呢 这个...本人文笔太差,我还是直接举例子吧. 相信玩过js童鞋应该都看过下面这样一个面试题 ...

  7. Javascript模块化编程之Why

    说到模块化编程,大家比较容易想到Java, C++等语言,感觉这事和Javascript沾不上一丁点边.虽说Javascript看上去好像同Java有莫大的关系,但那也只是一厢情愿,不过是挂羊头卖狗肉 ...

  8. JavaScript模块化编程之AMD

    简单的说一下AMD是"Asynchronous Module Definition"的缩写,意思就是"异步模块定义".它采用异步方式加载模块,模块的加载不影响它 ...

  9. Javascript模块化编程之CommonJS,AMD,CMD,UMD模块加载规范详解

    JavaSript模块化 在了解AMD,CMD规范前,还是需要先来简单地了解下什么是模块化,模块化开发?     模块化是指在解决某一个复杂问题或者一系列的杂糅问题时,依照一种分类的思维把问 题进行系 ...

随机推荐

  1. HDU-1828 Picture(扫描线 求矩形并的周长)

    http://acm.hdu.edu.cn/showproblem.php?pid=1828 Time Limit: 6000/2000 MS (Java/Others)    Memory Limi ...

  2. 用logstash 作数据的聚合统计

    用logstash 作数据的聚合统计 以spark-streaming 处理消费数据,统计日志经spark sql存储在mysql中 日志写入方式为append val wordsDataFrame ...

  3. 在服务器搭建git服务器

    服务端操作 安装Git及创建用户组.用户 123 yum install gitgroupadd gitadduser git -g git 禁止用户git登录 修改/etc/passwd文件: 找到 ...

  4. asp.net---jquery--ajax 实现滚动条滚动到底部分页显示

    前台:aspx页面 var bgtime = $(" #date1 ").val(); var overtime = $(" #date2 ").val(); ...

  5. 以elasticsearch-hadoop 向elasticsearch 导数,丢失数据的问题排查

    实际这是很久之前的问题了,当时没时间记录 这里简单回顾 项目基于 数据架构不方便说太细,最精简的 somedata-> [kafka]->spark-stream->elastics ...

  6. deeplearning.ai 人工智能行业大师访谈

    Geoffrey Hinton 1. 怀揣着对大脑如何存储记忆的好奇,Hinton本科最开始学习生物学和物理学,然后放弃,转而学习哲学:然后觉得哲学也不靠谱,转而学习心理学:然后觉得心理学在解释大脑运 ...

  7. mysqli存储过程

    <?php$link = mysqli_connect('localhost','root','','chinatupai');  $sql = "call getEmail('000 ...

  8. SpringMVC源码剖析2——处理器映射器

    1.处理器映射器 HandlerMapping 一句话概括作用: 为 我 们 建 立 起 @RequestMapping 注 解 和 控 制 器 方 法 的 对 应 关 系 . 怎么去查看 第一步: ...

  9. tomcat启动不了的问题

    tomcat启动的几个问题 1.端口冲突 2.非端口冲突,需要加入配置host文件 日志文件: 解决办法:https://blog.csdn.net/u012949658/article/detail ...

  10. HDU-1403-Longest Common Substring(后缀数组的高度数组运用)

    这题要求两个串中的最长相同子串的长度.高度数组可以求一个串中的最长相同子串的长度.所以想到把两个串连起来,但是这样又会产生一些新的串(第一个串的结尾和第二个串的开头组成的)于是在两个串中间放一个'\0 ...