摘要:事實上,實現元編程有多種方式,從語言本身來講,可以分為兩類增強型與新的語法實現,前者的代表是反射,后者的代表為。在第二部分,我們嘗試在語言基礎上增加原生的元編程能力并介紹了該思路的實現框架。
語言的自由度
自由度這個概念在不同領域有不同的定義,我們借鑒數學中構成一個空間的維數來表達其自由度的做法,在此指的是:解決同一個問題彼此不相關的設計方法學數量。
例如,解決一個比如商品打折的問題,如何設計順序、提取函數,具體的思路可能有很多,但是這可能都是從面向過程(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);};"http://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框架。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/51776.html
摘要:事實上,實現元編程有多種方式,從語言本身來講,可以分為兩類增強型與新的語法實現,前者的代表是反射,后者的代表為。在第二部分,我們嘗試在語言基礎上增加原生的元編程能力并介紹了該思路的實現框架。 語言的自由度 自由度這個概念在不同領域有不同的定義,我們借鑒數學中構成一個空間的維數來表達其自由度的做法,在此指的是:解決同一個問題彼此不相關的設計方法學數量。 例如,解決一個比如商品打折的問題,...
摘要:第章元編程與注解反射反射是在運行時獲取類的函數方法屬性父類接口注解元數據泛型信息等類的內部信息的機制。本章介紹中的注解與反射編程的相關內容。元編程本質上是一種對源代碼本身進行高層次抽象的編碼技術。反射是促進元編程的一種很有價值的語言特性。 第12章 元編程與注解、反射 反射(Reflection)是在運行時獲取類的函數(方法)、屬性、父類、接口、注解元數據、泛型信息等類的內部信息的機...
摘要:基本語法我們通過注解的定義來切入注解的語法。跟定義接口差不多,就是用到的是,然后加上了元注解。那么元注解的作用是什么呢元注解元注解說明了注解所修飾對象的類型。也就是標識該注解可以被繼承。的內置注解重寫了父類的方法表示已過時,不推薦使用。 在Android開發中我們經常會用到注解,例如@Override Butterknife中的BindView等。這里主要記錄下注解怎么寫和簡單的使用。...
摘要:注解提供了一種安全的類似注釋的機制,用來將任何的信息或元數據與程序元素類方法成員變量等進行關聯。為程序的元素類方法成員變量加上更直觀更明了的說明,這些說明與程序的業務邏輯無關,并且提供給指定的工具或框架使用。 什么是注解? Annotation 是 Java5 之后開始引入的新特性,中文為注解。注解提供了一種安全的類似注釋的機制,用來將任何的信息或元數據(metadata)與程序元素(...
閱讀 2929·2021-10-14 09:43
閱讀 2867·2021-10-14 09:42
閱讀 4640·2021-09-22 15:56
閱讀 2355·2019-08-30 10:49
閱讀 1587·2019-08-26 13:34
閱讀 2370·2019-08-26 10:35
閱讀 591·2019-08-23 17:57
閱讀 2023·2019-08-23 17:15