天天看點

Language Oriented Programming : The Next Programming Paradigm

Language Oriented Programming : The Next Programming Paradigm

Sergey Dmitriev, JetBrains

現在是軟體開發中開始下一次技術革命的時候了,而 這次革命的輪廓正變得越來越清晰。下一代程式設計範型也在接近我們,但仍然沒有完全成形--不同的部分有不同的名稱:Intentional programming, MDA, generative programming, 等等;我建議把把所有這些新方法歸并為一個名字: ‘language-oriented programming’(面向語言的程式設計), 而本文将闡述這種新的程式設計範型的主要原則

今天主流的程式設計方法有一些内在的假定像脖子上的繩 索一樣桎梏着我們,盡管大部分程式員還沒有意識到它;即使算上在程式設計領域取得的所有進步,我們也仍然處于石器時代;我們有我們信賴的石斧(面向對象編 程),能夠滿足我們的需要,但是當用它來對付最困難的問題時,它會裂成碎屑;為了超越石器前進,我們必須馴服烈火,隻有這樣,我們才能鑄造出新的工具,激 發一個創作的新時代,和新技術的爆發

我将讨論程式設計的局限,它強迫程式員像計算機一樣思考,而不是令計算機像程式員一樣思考;這是嚴重的,根深蒂固的局限,需要花費巨大的努力去克服它;當我說這将是程式設計中下一個大的範型轉換時我并沒有自命不凡;我們需要徹底重新定義我們編寫程式的方法

本文中,我表述了我的觀點和我目前在 Language Oriented Programming (LOP)上的工作;首先我将展示目前主流程式設計方法的錯誤,然後我會使用示例來解釋LOP的概念,它們基于我已有的一個LOP的實作:the Meta Programming System (MPS). 本文有意隻是給你一個對LOP的驚鴻一瞥,目的是激發你對這個思想的興趣,并希望能夠得到回報和讨論

Part I. LANGUAGE ORIENTED PROGRAMMING OVERVIEW

Language Oriented Programming and the Meta Programming System

理想的,做一個程式員意味着我可以對計算機做任何事情,我有完全的自由,完全的控制;但實際上,今天的程式員隻有非常受限的自由;當然,我确實可以在計算機上做任何事情,但其中一些事情花費了我許多年的努力,而它們實際上隻需要少的多的時間;一定有什麼事情不對勁

程式員被限制是因為他們深深依賴于那些他們不能輕 易改變的程式設計基礎設施:程式設計語言和開發環境;如果我需要一些語言的擴充,我隻能等待語言的設計者去更新它;如果我需要我的IDE有一些額外的強大功能,我 隻能等待供應商來添加新特性;就是這些依賴限制了我完全的自由;當然,我可以寫我自己的編譯器和IDE,實際上,這也是我啟動了IntelliJ IDEA的原因,因為我厭倦了依賴現有的弱弱的Java IDE;但是,這會花費大量的時間和努力,并且顯而易見,對大部分程式員來說是不可行的;理論上的自由和實際的自由之間存在巨大的差異;下文中當我談到自 由時,我指的是實際的自由

獲得自由的途徑是減少我們的依賴層次;例如,Java的一個主要目标是減少對作業系統的依賴,給開發者在不同作業系統上部署的自由;是以,為了在語言和環境之上獲得自由,我們需要減少對它們的依賴

為什麼這是一個問題呢?任何general- purpose的語言,像Java和C++,給了我們用計算機做任何事情的能力;這是事實,至少理論上是這樣,但是,general-purpose的語 言趨向于如同後面我将講到的般生産效率低下;作為一種替代,我們可以使用domain-specific languages(DSLs,aka ‘little languages’),它們被精心設計成在特定問題域具有高度生産率,比如用SQL編寫資料庫查詢;DSLs的強大之處,領域相關,也正是它們的弱處, 因為任何真實世界中的程式都會包括許多不同的領域

general-purpose 和 domainspecific 之間,并不是對立的;我需要所有的自由,我希望能夠做任何事情,同時有很高的生産效率;目前為止還沒有什麼好方法能夠做到這點;理想情況下,我能夠為程式 的各個特定部分使用不同的語言,而它們能夠融洽的一起工作,并且開發環境會完全支援這些語言,包括重構,代碼補全,導航,以及主流語言具有的所有其它生産 力工具

為了獲得這種獨立性,我需要有建立、重用、修改語 言和環境的自由;為了使這種自由是可行的,它需要很容易的被獲得;如果我們解決了易于進行語言和環境的開發的問題,對程式員來說将是一個巨大的進步;這就 是Language Oriented Programming的切入點

要了解Language Oriented Programming是什麼,讓我們首先看一下今天的主流程式設計方法,它基本上是這樣:

思考: 你需要程式設計解決一個問題,是以你在你的頭腦裡形成了如何解決這個問題的概念模型

選擇: 你選擇了某種general-purpose的語言來編寫解決方案

程式設計: 你通過将你的概念模型艱難的映射到程式設計語言來編寫解決方案

程式設計這一步是瓶頸所在,因為大部分情況映射不是容易的和自然的;這種方法在程式員表達複雜的設計方面已經被證明是低效的;相對的,下面是LOP的工作方式:

思考: 你需要程式設計解決一個問題,是以你在你的頭腦裡形成了如何解決這個問題的概念模型

選擇: 你選擇了某些特定的DSLs來編寫解決方案

建立: 如果沒有合适的DSL适合你的問題,你便建立一種DSL來适應你的問題

程式設計: 你通過将你的概念模型相對直接的映射到DSLs來編寫解決方案

現在,程式設計這一步is much less of a 瓶頸了,因為DSLs大大簡化了如何将問題翻譯成某種計算機能夠了解的東西;看起來困難已經簡單的轉移到了“建立”這一步,然而,通過聯合使用工具支援和将LOP應用到自身,将使這一步更加簡單

LOP背後的動機基本是這樣的:我想用我正試圖解 決的問題相關的概念和意圖的詞彙來工作,而不是被迫将我的思想翻譯成某種general-purpose的語言所能了解的概念(比如:類,方法,循環,條 件,等等...);為了達到這個目标,我需要使用domain-specific languages;怎樣得到它們呢?建立它們;

我已經開始開發一個通用的平台(the Meta Programming System)來設計domainspecific languages,帶有工具支援和開發環境;它将允許程式員像現在編寫程式一樣容易的來定義語言;這個平台将完全支援LOP,給程式員為程式的每一部分 選擇使用最合适的語言的自由,而不是将他們綁在某種固定的general-purpose的程式設計語言上

MPS隻是Language Oriented Programming的一個示例,盡管我在這裡使用MPS來做示例,而實際上LOP可以用許多不同的方法來實作,你自己就可能知道一些替代方法;LOP 的概念不等同于它的實作,就像OOP的概念不等同于Java或C++或Smalltalk一樣

What Is Wrong with Mainstream Programming

你知道這則古老的諺語:"If it ain't broke, don't fix it". 主流程式設計方法很明顯不完整,我見過它帶來的很多問題,而大部分滋生于這樣一個事實:general-purpose的語言沒有一種方法來完全支援任意的領 域,同樣也沒有一種統一的domain-specific language;下面是将被LOP解決的主流程式設計中三個最糟糕的問題:

Time Delay to Implement Ideas

對我來說,最嚴重的問題是,在我确切的知道如何解 決一個問題,和我通過一個程式成功的向計算機傳達解決方案之間,有一個很長的時間差;我可以用幾個小時的時間向另外的程式員解釋問題和解決方案,而将解決 方案編碼到計算機中将花費長的多的時間;這是因為對另外的程式員,我可以使用表達能力非常豐富的自然語言,而對計算機,我隻能使用某種表達能力差很多的 general-purpose的程式設計語言;今天的程式設計語言隻能表達幾十種概念,而自然語言能夠簡潔的表達千萬種概念;是以,向另外的程式員解釋問題,我 可以表達很高層的思想,而對計算機,我必須表達每一步的每一個細節

在主流程式設計中,大部分花在“程式設計”上的時間,實際上是在尋找用程式設計層次的抽象的術語來表達自然語言的概念的方法,而這是很困難的,沒多少創造性的,或多或少是一種時間的浪費

舉個例子,今天大量的開發時間花費在面向對象的設 計(OOD)上,在程式員表達類、繼承、關聯等方面這确實是一種還算有創造性的過程;這項實踐的目的是用面向對象的術語,如類和方法,來表達程式;OOD 的過程是必要的,因為諸如類和方法等是面向對象語言能夠了解的僅有的抽象,它看起來是必要和有創造性的,但是使用Language Oriented Programming,OOD根本就不需要

Understanding and Maintaining Existing Code

下一個問題是了解和維護現存代碼;不管它是另一個 程式員寫的還是我寫的,問題都一樣;因為general-purpose的語言需要我把高層的領域概念翻譯為低層的程式設計語言特性,在最終的程式中,很多高 度概括的視角、藍圖都丢失了;當我在以後重新翻閱程式時,我不得不通過逆向工程來了解我最初的意圖是什麼,我頭腦中的模型是什麼;至少,我必須在腦海中重 建立造最初在翻譯到general-purpose的程式設計語言的過程中丢失的資訊

解決這個問題的傳統方法是寫注釋或其它形式的文檔 來記錄設計資訊和模型資訊,已經有幾個方面的因素證明了這是一種脆弱的解決方案,至少包括編寫這些輔助文檔的成本、以及文檔和代碼逐漸不同步的趨勢;并 且,還有一個沒被廣泛認識到的事實,就是文檔并不能直接連接配接到它所記錄的概念;注釋和源代碼被綁定到同一個地方,但是概念可能在源代碼的多個地方被表達; 其它類型的文檔徹底從源代碼中分離出來,隻能間接的引用源代碼;理想情況下,代碼應該是自我描述的,我應該隻閱讀代碼本身來了解代碼,而不是什麼注釋和外 部的文檔

Domain Learning Curve

第三個主要的問題是對語言進行領域相關的擴充;例 如,在OOP中擴充語言的主要方法是使用類庫;問題是類庫不是用領域概念相關的術語來表達的,而是用低層的general-purpose的抽象諸如類和 方法等來表達;是以,庫很少能夠直接表述領域概念,它們必須引入額外的枝節(如一個類的運作時行為)來完成到領域概念的映射;兩個很好的常見例子是GUI 庫和Database庫

學習這些類庫不是一項簡單的任務,即使你是個領域 專家;因為從領域到語言的映射不是直接的,你必須學習這種映射;這意味着一個陡峭的學習曲線;通常我們試圖用大量的指南和文檔來解決這個問題,但是學習這 些将花費大量時間;當一個類庫變得複雜的時候,它也變得更難以學習,程式員将是以失去學習它的動機

甚至當掌握了這種複雜的映射之後,依然還會很容易 的誤用類庫,因為開發環境(像編譯器和編輯器)不能幫助你正确的使用類庫,對這些工具來說,調用一個GUI對象的方法和調用一個DB對象的方法是一樣的: 它們都隻是對象上的方法調用,沒有任何更多的意思;記住哪些類和方法應該被調用,以什麼順序被調用,等等,都是使用者的責任

甚至即使你既是領域專家又是類庫的使用專家,也仍然有使用類庫編寫的程式十分冗長的問題;相對簡單的領域概念需要複雜的措施來正确的調用;例如,任何用過Swing的開發者都清楚這一點;編寫簡單的程式就已經花費太長的時間了,複雜的程式甚至更糟

Details of LOP

What Is a Program in LOP?

今天,百分之九十九的程式員認為程式設計就是編寫一串 計算機能夠執行的指令集;我們被教育說計算機建立在圖靈機模型之上,是以它們用指令集的術語來“思考”;但是這種程式設計的觀點是有缺陷的,它混淆了程式設計的目 的和手段;我将為你示範LOP為什麼優于傳統程式設計方法,但首先我必須澄清以下事實:一個LOP的程式,不是一串指令集;那麼它是什麼呢?

當我有一個問題要解決,我在頭腦中思考解決方案, 這個解決方案用單詞、标記、概念、思想,或者任何你喜歡的稱呼來表述,它是我頭腦中如何解決問題的模型;我幾乎從未把它們想象成一堆指令集,而是我正在工 作的領域中特定的具有内在聯系的概念的集合;例如,當我思考GUI領域時,我想象“這個按鈕到那邊去,這個輸入域到這邊來,這個組合框裡面需要有一些資料 的清單”;我甚至隻是在頭腦中把它畫出來,根本不用任何言語

我之是以認為這種意念模型是一種解決方案是因為我 能夠用足夠的細節向另一個程式員解釋這個模型,使他能夠坐下來編寫一個解決這個問題的程式(比如用Java);我不需要非得用程式設計語言的術語來解釋這個方 案,它可以是任意形式;比如,為了解釋如何布局一個GUI的窗體,我隻需要畫出這個窗體;如果繪畫有足夠的細節,繪畫本身就代表了解決方案;這種領域相關 的表述應該就是程式。換句話說,應該有一種方法允許我們使用這種表述作為真正的程式,而不僅僅是與其它程式員交流的手段;于是這便導出了我對程式非正式的 定義:一個程式是任何對一個問題無歧義的解決方案,或者,更精确一點:一個程式是對某個領域的某個問題的解決方案的任何使用領域相關概念表達的,精确定義 的模型

這就是我認為程式員應該擁有建立他們自己的語言的 自由的主要原因:這樣他們就能夠用更加自然的形式來表達解決方案;General-purpose的語言是無歧義的,但是太備援和易于出錯;自然語言(如 英語)表達能力十分豐富,但目前它難以使用因為它太不精确太不形式化了;我們需要能夠容易的建立形式化的,精确定義的,領域相關的語言;是以 Language Oriented Programming将不隻是編寫程式,還包括建立用來編寫程式的語言;我們的程式将被編寫的更接近問題域而不是計算機指令集領域,是以它們将非常容易 的被編寫

Programs and Text

人們習慣性的認為程式是作為文本來存儲的,也就是 說,一個位元組流;為什麼不應該是呢?畢竟有無數的工具來編輯、顯示、操作文本;今天的程式設計語言的核心部分是文法器,解析器,編譯器和面向行的調試器;但是 程式的文本隻是程式的一種表現形式;程式不是文本;強行把程式塞到文本裡引起了大量你可能還不知道的問題;我們需要一種不同的方法來存儲并和我們程式一起 工作

當編譯器編譯源代碼時,它把文本解析成稱作抽象語 法樹的樹狀結構;當程式員閱讀源代碼時,他們在腦海中做了本質上相同的事情;我們仍然不得不考慮程式的樹狀結構;這就是為什麼我們要有花括号,方括号,圓 括号等;這也是為什麼我們需要格式化和縮進代碼和遵守編碼規範,因為這樣就能夠更容易的閱讀源代碼

我們為什麼使用文本存儲呢?因為目前,閱讀和編輯 程式最友善和最通用的方法還是使用文本編輯器;但是我們會為此付出代價,因為程式的文本表示有重大的缺點,其中最重要的是基于文本的程式設計語言非常難于擴 展;如果程式以文本的形式存儲,你就會需要一個無歧義的文法器來解析程式;當為語言加入新特性時,維護語言無二義性的擴充變得日益困難;我們将需要發明更 多類型的括号、操作符、關鍵字、順序規則、嵌套,等等;語言的設計者們花費了無數時間來思考文法,并試圖發現擴充語言的新方法

如果我們打算讓建立語言變得容易,我們就需要将程 序的表示和存儲從程式本身分離開;我們應該直接将程式存為結構圖,因為這允許我們對語言做任何我們喜歡的擴充;有時,我們甚至根本不需要考慮文本存儲;今 天的一個很好的例子是Excel spreadsheet.百分之九十九的人根本不需要處理存儲格式,當這成為問題時總會有各種導入導出功能可用;今天我們使用文本的真正原因是我們沒有比 文本編輯器更好的編輯器,但是我們可以改變這一點

問題是文本編輯器很愚蠢,并且不知道如何與程式的 圖狀結構一起工作;但是使用正确的工具,編輯器将能夠直接和圖狀結構一起工作,并且能夠讓我們自由的使用任何編輯器提供的我們喜歡的可視化表現形式;我們 可以把程式做成文本、表、圖、樹、或其它任何形式;我們甚至能為不同目的使用不同的表現形式,比方說,圖形化表示用來浏覽,文本化表示用來編輯;我們能夠 為代碼的不同部分使用領域相關的表示,比如為數學公式使用圖形化的數學符号,為圖表使用圖形化的圖表,為spreadsheets使用行和列,等等;我們 能夠為問題域使用最合适的表現形式,可以是文本,但不限于文本;最好的表現形式依賴于我們如何思考問題域;表現形式的靈活性也将使我們的編輯器比以往更加 強大,因為不同的表現形式有不同的方式去編輯它們

What Is a Language in LOP?

最後,我應該闡明我認為的“語言”是什麼;在 LOP中,一種語言是通過三個主要的要素來定義的:結構、編輯器、和語義;結構定義了抽象文法、支援的概念、以及如何安排它們;編輯器定義了具體的文法, 如何描繪和編輯語言;語義定義了行為,它如何被解釋,和/或它如何被轉換成可執行代碼;當然,語言還可以有其它方面,比如限制和類型系統

Part II. INTRODUCTION TO META PROGRAMMING SYSTEM

Creating Languages in MPS

我已經解釋了為什麼我們需要容易的建立新的語言, 但是,我們如何才能讓它容易呢?如果你turn around這個問題,并且把Language Oriented Programming應用于它自身,你會很快看到答案;This calls for a little self-referential bootstrapping, which can seem tricky, but be patient. 一旦你了解了這個,你将得到LOP真正的力量(一個LOP的元層次)

回顧一下LOP的理念:使建立DSLs更容易,而 這些DSLs将使編寫程式更容易;但就像我已經說明的,LOP中的‘程式’不局限的意味着你用過的典型的“一堆指令集”的程式;對某個領域中某個問題任何 無二義性的解決方案都是‘程式’;是以如果你設想一下“建立新語言”這個領域,那麼這個領域中的‘程式’,本身就是一種新語言的定義,可以作為一個解決方 案來思考,就像任何其它領域的解決方案一樣;

是以,應用LOP的思想,使“建立新語言”更容易 的方法,就是建立一種特定的專注于“建立新語言”這個領域的DSL;通過應用這些language-building DSL,我們可以使制造新語言更容易;讓我們看幾種language-building語言的例子,使你更好的了解它們是如何工作的;這裡隻是一個概述, 以後的文章我會更詳細的描述它們

Structure Language

最小最少,我們需要定義新語言的‘結構’;這是我們何以能夠編寫“精确定義”的程式的原因;語言的結構并不意味着它的文本形式的文法--像我提到過的,這種語言甚至根本就沒有文本表示而隻有圖形化表示

在實踐LOP的時候,大部分情況下,你會工作在兩 個層次的程式設計中:元層次和程式層次;你在元層次中定義語言,在程式層次中編寫程式;當定義一種新語言的結構時,你會使用一種language- structure DSL來定義你的新語言,而這時,你将同時工作在這種language-structure DSL的程式層次和新語言的元層次中

在MPS中,程式層次的每個節點都有一種“類 型”,簡單的連接配接到元層次的另一個節點;程式層次的這個節點被稱作這種類型的一個“執行個體”;元層次中的“類型”節點則定義了這種類型的執行個體能夠擁有的關系 和屬性;描述這種元層次語言結構的語言,就被簡單的稱為“Structure Language”

用Structure Language定義一種語言的抽象文法,你應該隻是枚舉這種語言所有的類型;類型簡單的表示了這種語言支援的特性或者概念;每個概念應該用它的名字、執行個體的内部屬性、執行個體與其它節點之間的關系(通常是連接配接)來定義

存在兩種可能的關聯;第一種是類似聚合的關聯,它 形成了概念模型的父子樹結構;第二種是非聚合的,自由形式的關聯,它可以連接配接到系統中任何其它的節點;關聯有兩個端點:源和目标;關聯有角色,你可以定義 每個角色的名稱、每個端點的多重性,每個目标節點的類型;多重性可以是1, 0..1, 0..n, 1..n等,讓你能夠限制關聯可以建立多少連接配接;關聯的目标類型可以被用來限制哪些類型的節點可以被連接配接在一起

是以,使用新語言編寫程式包括:建立語言中概念的執行個體、為執行個體的屬性指派、根據語言概念定義的關系将程式中的節點連接配接在一起;所有這些将會被強大的編輯器支援,你能夠為你的語言定義這種編輯器

Editor Language

那麼,編寫和操作概念模型的界面應該是什麼呢?我 們的語言需要幾種類型的編輯器,但是我們不想要一個通用的編輯器;經驗表明通用的編輯器不能像我們希望的那樣有用;我們希望快速的編寫模型,是以我們需要 專為我們的語言概念定做的特殊的編輯器;一定程度上,編輯器是語言的一部分,而我們的目标是容易的建立新語言,那麼建立新的編輯器也應該很容易;本質上, 我們需要一種建立編輯器的語言,在MPS中,它被稱為Editor Language

當人們聽我說我們的程式将存儲為圖形并且我們需要 特定的編輯器,我确信很多人認為我将要談到圖形編輯器,事實不是這樣子的;盡管程式是圖形形式,編輯器卻不一定非得将程式描繪成圖形;事實上,隻有少數情 況下圖形編輯器才是有用的(也就是說,當它合适的時候,比如對資料庫表);相反,我們的Editor Language有更好的靈感來源,諷刺的是,它來自文本編輯器

如果你用文本編輯器浏覽一個典型的程式,你可以想 象編輯器被分成了矩形單元;一些單元包含必需的辨別如關鍵字、花括号、圓括号等,其它的單元包含使用者定義的辨別,如類和方法的名稱;大的單元由小的單元組 成,像方法塊包含語句,而語句可能包含自己的嵌套塊;事實上,任何主流程式設計語言中任何良好構造的程式都可以分解為矩形單元的集合;那麼,在Editor Language中,你不需要想象這些單元,因為編輯器就是簡單的由矩形單元組成的

單元的使用有一些有趣的優點;首先,當直接工作在 程式圖形而不是程式文本上時,單元可以完美的模仿甚至超過标準的文本編輯器;第二,單元不局限于文本,你可以往單元裡塞進顔色選擇器、數學符号、圖表、矢 量圖、或任何别的什麼;最後,這種單元形式的layout是可選的,程式員可以提供不同的機制,單元形式的layout隻是一種有用的預設設定

是以,Editor Language幫助你定義語言中每個概念對應的單元的layout;你可以定義哪些部分是不變的,像括号或其它修飾符号,哪些是可變的,需要使用者去定義 的;Editor Language也幫助你在你自己的編輯器中加入強大的特性,像自動完成、重構、導航、文法加亮、錯誤加亮、以及任何其它你想到的事情;是以你能夠增加現 在的編輯器如IntelliJ IDEA等擁有的功能到你自己的語言中;這是可能的,因為程式和語言被構造為圖形,而我們有專門的Editor Language幫助我們創造強大的編輯器

Transformation Language

Structure Language和Editor Language已經共同提供了一些功能,你能夠用它們和其他人交流思想,比如畫UML圖,或者編寫其它類型的文檔;然而,大部分時間我們是想讓我們的代 碼做點什麼,是以,我們必須找到一種方法讓它能夠執行;有兩種主要的方式來做這件事情:解釋和編譯

DSLs支援的解釋方式幫助定義計算機應該如何解釋程式,DSLs支援的編譯方式幫助定義如何為程式産生可執行代碼;我将在以後的文章中讨論對解釋方式的支援,現在我想說明一下MPS是如何支援編譯方式的

編譯意味着拿到源代碼,并從中産生某種形式的可執 行代碼;對于結果代碼有多種可能的形式;為産生可執行代碼,你可以生成本地機器碼,也可以生成虛拟機位元組碼;或者,你可以生成另外一種語言的源代碼(比如 Java,C++),然後用現有的編譯器轉換為可執行代碼;類似的,你甚至可以産生某種解釋型語言的源代碼,用現有的解釋器解釋執行

為了避免處理這麼廣泛的目标格式,我們的方法是用 MPS來做每一件事;首先,你在MPS中使用Structure Language定義一種目智語言,這種目智語言和目标格式之間應該有直接的一對一的映射;例如,如果你的目标格式是機器碼,你應該用MPS定義一種對應 機器碼的目智語言;如果目标格式是Java源代碼,你應該定義一種類Java的目智語言;目智語言不必支援目标格式所有的功能特性,隻為你需要的語言特性 進行簡單的一對一的映射即可

那麼現在,編譯分為兩個階段:一個簡單的從目智語 言到最終結果的翻譯,一個更複雜的從最初的原始語言到中間目智語言的轉換;翻譯階段是微不足道的,是以我們把精力集中于更有意思的轉換階段;至少,現在的 問題簡化為了如何将模型從一種語言轉換到另一種語言;但是,有可能源語言與目智語言是完全不同的,導緻轉換非常複雜,比如映射一個源節點到許多散布在目标 模型中的目标節點;我們想讓定義轉換盡可能的簡單容易,是以我們需要一種模型轉換DSL來幫助我們;在MPS中,這種DSL被稱為 Transformation Language

代碼生成有三種主要的方法,我們将結合使用它們來 定義模型轉換;第一種是周遊方式,你枚舉源模型中所有節點,檢視每一個,并基于檢視到的資訊生成目标模型中的一些目标節點;第二種方式是使用模闆和宏來定 義如何生成目智語言;第三種方式是使用模式比對來查找在源模型中的哪些節點上應用轉換

我們通過定義DSLs把這些方式結合起來以支援任 何一種方法;這些DSLs将一起工作來幫助你定義從一種語言到另一種語言的轉換;例如,周遊方式激發了Model Query Language的靈感,它使枚舉節點和從概念模型中收集資訊變得簡單容易;你可以把它想象成某種針對概念模型的SQL;做為一種額外的獎賞,擁有一種強 大的查詢語言不隻是對代碼生成有用(例如,能夠使編輯器更聰明)

Templates

模闆方法工作方式類似Velocity或者 XSLT;模闆看起來很像目智語言,但是允許你在模闆的任何部分中添加宏;宏本質上是當運作轉換的時候被執行的代碼段;宏允許你檢視源模型(使用 Model Query Language),并使用得到的資訊對模闆進行“填空”,得到最終的目标代碼

在圖5中,你可以看到為概念“Property”生成Java代碼的模闆的定義,模闆為屬性添加了field declarations, getters, setters等;這個模闆是将代碼從Structure Language轉換為Java的生成器的一部分

既然模闆看起來像目智語言,你可以想象模闆是用某 種基于目智語言的特殊的語言編寫的;這也是它事實上的工作方式;我們實際上使用一個生成器來為你生成模闆語言,而不是手工為每一種可能的目智語言建立模闆 語言;它基本上是複制目智語言,并添加所有模闆特定的特性,諸如宏等;甚至模闆編輯器也是從目智語言編輯器産生的,是以你同樣不需要處理代碼

當你使用一種模闆語言的時候,你可以認為它是用目智語言編寫的,隻是某些部分的代碼是參數化的,或者是由宏來計算的;這種技術極大的幫助簡化了代碼生成;模闆還可以用在其它任務上,如重構、代碼優化、還有更多...

Patterns

模型的模式比對方法給我們一種作為Model Query Language的代替的查找模型的強大方法;你可以把模式想象成概念模型的正規表達式;與模闆方法類似,我們基于源語言産生模式語言;模式語言看起來像 源語言,隻是添加了一些特性,來幫助你定義處理複雜源模型比對的靈活的标準;你可以把這種方法想象成一種強大的“查找替換”的技術;再一次,模式語言不隻 是對代碼生成有用,例如,它們在為源語言編輯器編寫自動化的代碼檢查工具方面非常有用

記住Model Query Language, template languages, 和pattern languages都由強大的編輯器支援其自動完成、重構、引用檢查、錯誤勘測、等等;即使複雜的查詢、宏、模式,都可以很容易的編寫;代碼生成從來沒有 這麼強大過

Using Languages Together

前面有關代碼生成的章節帶來了一些關于這些語言如何一起工作的有意思的問題;事實上有幾種方法能讓語言一起工作;在MPS中,所有的概念模型都互相知曉;既然語言也是概念模型,那麼便意味着所有的語言都彼此知曉,可以潛在的被連接配接在一起

語言彼此之間可以有不同的關系;你能夠通過擴充現存的語言來建立新語言,繼承所有的概念,修改其中的一些,并加入你自己的概念;一種語言可以引用其它語言中的概念;你甚至能将一種語言插入到另一種語言中去;我将在以後的文章中讨論進一步的細節

Platforms, Frameworks, Libraries, and Languages

我們支援Language Oriented Programming的系統需要比元程式設計能力更多的功能才能更有用;它應該提供程式員依賴于目前的程式設計語言提供的所有事物:集合,使用者界面,網絡,資料 庫連接配接,等等;程式員不止是單單基于語言本身來選擇語言;例如,Java的大部分功能不是語言提供的,而是有成千上萬的framework和API供 Java程式員選擇;他們買的不是Java語言,而是整個Java平台;MPS也将有一個它自己的支援平台

在我進入細節前,我們先簡要談一下frameworks;什麼是framework?在主流的程式設計中,它通常意味着一堆類和方法打包成的一個類庫;讓我們更近一點的觀察這一點,看看在LOP的鏡片下我們會看到什麼

我們為什麼想要把類和方法打包成庫呢?程式員會背 誦他們的教授曾經告訴他們的:“複用”;但這隻是在原來的位置上留下了另一個問題:我們為什麼要複用類庫?答案是類庫在解決特定類型的問題方面很有用,如 制作使用者界面,通路資料庫等等;你可以說類庫對應着某個領域;你瞧,我們看到了聯系,Class libraries are wannabe DSLs!這個悲傷的事實真令我沮喪

今天的Domain-specific languages以類庫的形式存在,除了它們不是語言,沒有語言的優勢,并擁有類和方法所有的局限;特别的,類和方法直接綁定到了特定的運作時行為,而 特定的運作時行為是不能修改和擴充的,因為行為是提供“類”和“方法”的概念定義的;因為它們不是語言,類庫很少被環境(例如編譯器和編輯器)聰明的支援

我們應該忠于wannabe DSLs,還是應該擁有當需要DSLs的時候使用真正DSLs的自由?當然是自由;每個類庫都是為我們的平台建立一種完全的DSL的候選;例如,JDK的 所有類庫都應該是MPS平台上的DSLs;其中一些DSL在現在剛開始的時候不是那麼緊急需要,但其它一些從一開始就對平台的功能和可複用性有強烈的影 響;我将介紹随MPS提供的三種最重要的平台語言:The Base Language, the Collection Language, and the User Interface Language

Base Language

我們首先需要的語言是對應最簡單程式設計領域的,一種general-purpose的指令式程式設計;這種簡單的語言應該支援近乎通用的語言特性諸如算術、條件、循環、函數、變量等等;在MPS中我們有這樣一種語言,它被稱為Base Language

對這種語言的需求應該是很明顯的,例如,如果我們想把兩個數字加在一起,我們應該能夠簡單的說一句“a + b”就可以;我們不需要到處去使用它,但幾乎所有的程式都會有一些組成部分用到它,在那裡,它是完成工作最合适的工具

Base Language之是以如此命名,是因為它是很多需要基本程式設計支援如變量、語句、循環等的語言很好的基礎;它能夠以三種方式使用:你可以擴充它以建立出你 自己的基于它的語言,你可以在你的程式中引用它的概念,你還可以以Base Language生成你的代碼;将會有幾種可用的生成器來将Base Language轉換成其它語言如Java,C++等;當然,不是每種語言都需要使用Base Language,但是在很多情況下,它是一個很好的起點

Collection Language

下一種我們需要的最重要的語言是和集合一起工作的 語言;對集合支援的需求是普遍存在的;每種主要的主流語言都提供了對集合某種類型的支援,例如,在Java中你有java.util,在C++中你有 STL;每個人都需要集合;如果每種DSL都提供自己的對集合的支援,那麼将會有a Babylon of不同的集合語言,它們互不相容;這就是為什麼MPS必須提供一種每個人都使用的單一的Collection Language的原因

在很多主流語言中,集合并非語言特性而是類庫,一個例子是Java的java.util包;這種支援技術上來說是存在的,但它是不友善的,雜亂的,并且易于出錯的

Yuck!今天大部分的Java代碼被一行接一行 多餘的、重複的處理集合的代碼弄的雜亂無章;圖6顯示了一個例子,Collection Language是如何beats the tar out of a 類庫的;例子是一個計算一組給定的點的convex hull的算法;更多關于Collection Language的細節會在以後的文章中提及

User Interface Language

User Interface Language是我們的平台中下一種最重要的DSL;有趣的是,我前面提到的Editor Language能夠另人信服的用來提供使用者界面,但是一種專為圖形使用者界面設計的語言将會更靈活;這種語言帶來的益處是巨大的;Java Swing代碼就是一個想成為DSL的類庫的極好的例子:功能有了,但很容易被誤用,并且Swing的代碼是徹底雜亂的;很多如今的開發環境都包含GUI builder來簡化使用者界面的建立;User Interface Language将把這項任務帶到一個更高的層次;我将在以後的文章中讨論更多細節

Getting Started with MPS

我已經能夠聽到一些對LOP懷疑的反應:“聽起來 不錯,但是我們的項目已經步入正軌,現在切換到LOP是不可行的”,或者“聽起來不錯,但用一個像LOP這樣的未經檢驗的方法來啟動一個現實生活中的項目 風險太大了”,或者“聽起來不錯,但是它什麼時候才能為它的黃金時期做好準備呢?别忘了OOP用了20年才成為主流”

好消息是我們不需要一頭紮進未知裡,你可以先用腳趾頭試一下水;你可以在你的項目中隻是應用LOP的一小塊來看一下它有沒有提供一些實際的好處,然後如果你喜歡你可以用多一點;在不遠的未來,你可以在MPS裡試驗兩個可能的LOP應用:

Using MPS on Java Applications

已經有一個IntelliJ IDEA的原型插件允許你在你的項目中包含MPS的概念模型;當你編輯模型時,模型會自動在背景被轉換成Java源代碼;是以,你可以使用MPS來編寫 Java應用的部分子產品,喜歡用多點就用多點,喜歡用少點就用少點;這意味着你得到了MPS全部的力量,比如建立和使用特定DSLs的能力,做任何你想要 的語言擴充,同時使用定制的帶有自動完成、錯誤加亮、重構功能的編輯器,等等;插件将和IDEA緊密內建,允許你在你的MPS模型中嵌入Java代碼,導 航到嵌入或生成的Java代碼,甚至進行概念層次的調試,就像IDEA中已經可用的JSP調試支援一樣;更多內建特性正在計劃中;這将是使用IDEA的 Java開發者可用的一個重要的新工具

Configuring and Scripting Your Applications

有一個我見過很多次的模式,一個應用程式啟動時需 要某種形式的配置,可能是一個簡單的配置檔案,或者更完整的部署描述符檔案;最後,配置變的更複雜,應用程式最後需要一種腳本語言;對于簡單的配置文 件,XML很流行;對于腳本語言,你可以建立自己的,或者借用一種general-purpose的腳本語言,像 VBScript,Python/Jyphon,Tcl,Javascript,或者Lisp;這些方案中的每一種都至少有一些主流程式設計方法的标準缺陷: 很長的實作時間,陡峭的學習曲線,難以擴充,匮乏的環境支援,等等

作為替代的,你能夠使用MPS建立你自己的配置/ 腳本語言;你的應用程式的使用者将會擁有一種易于使用的、智能的編輯器來編寫他們的腳本,包括文法加亮,錯誤加亮,代碼完成,導航等;隻需花費很少的時間來 建立并內建這種語言到你的應用中;為了使用這種應用,你可以分發MPS的運作時

Conclusion

LOP和MPS背後的思想并不新鮮,實際上已經出 現超過20年了;Language Oriented Programming本身這個詞也已經提出至少10年了;新鮮的是這些思想一直在軟體開發社群默默的滲透,而它們的時代最終到來了;通過這篇文章,我希 望提供一顆種子,使這些思想能夠産生新的讨論、意見、批評、實驗、研究、和最終的真實生活中的項目

并且,是以,我邀請你以任何你能做到的方式參與到這種新的範型中來;在後面添加評論,或者發送Email給我:[email protected] ;在http://www.jetbrains.com/mps 可找到更多,請關注更新;注意浏覽從LOP的視點出發的網站,雜志,部落格,書籍等,并思考事情到底能夠多麼簡單;考慮一下你自己的項目,看看你有多頻繁的實際上在設計和使用小的特定的用類和方法修補的語言;你是怎麼認為的呢?我想知道

當我将LOP的概念用在開發MPS自身時,我已經 看到了Language Oriented Programming是如何徹底改進軟體開發的第一手資料;MPS目前并沒有為真實世界準備好,但它已經成功的達到了目的;也還依然沒有文檔,除了這篇 文章;我将很快的釋出更多的文章,深入的探讨MPS;還有,我計劃下月試驗着使MPS可下載下傳,是以你的耳朵要保持張開; 已經有其它的項目使用了類似的方法,特别是來自Intentional Software 和 Xactium

是以,探險愉快,讓我看看你能發現什麼

Acknowledgements

I would like to thank Rob Harwood for his help in editing this article. I would also like to thank the following people for their reviews, comments, and suggestions: Igor Alshannikov, Florian Hehlen, Jack Herrington, Guillaume

Laforge, Vaclav Pech, Thomas Singer, Dmitry Skavish,David Stennett, and Timur Zambalayev.

About the Author

Sergey Dmitriev (http://www.sergeydmitriev.com) is the co-founder and CEO of JetBrains Inc.(http://www.jetbrains.com), makers of the IntelliJ IDEA Java IDE.

References

Articles:

[1] Donald E. Knuth. Literate programming. The Computer Journal, 27, 97-111, May 1984.

[2] M. Ward. Language Oriented Programming.Software - Concepts and Tools, 15, 147-161 1994,

http://www.dur.ac.uk/martin.ward/martin/papers/middle-out-t.pdf

Intentional Programming articles:

Charles Simonyi. The Death of Computer Languages, The Birth of Intentional Programming. 1995.

ftp://ftp.research.microsoft.com/pub/tr/tr-95-52.doc also

ftp://ftp.research.microsoft.com/pub/tr/tr-95-52.ps

John Brockman. Intentional Programming: A Talk With Charles Simonyi. Edge. 2000.

http://www.edge.org/digerati/simonyi/simonyi_p1.html

Microsoft Research. Intentional Programming.

http://www.cse.unsw.edu.au/~cs3141/ip.asf (video)

Charles Simonyi. Intentional Programming: Asymptotic Fun?http://www.hpcc.gov/iwg/sdp/vanderbilt/position_papers/simonyi.pdf

Books:

Krzysztof Czarnecki and Ulrich W. Eisenecker. Generative Programming: Methods, Tools and Applications.Addison-Wesley, 2000. ISBN: 0201309777.

Jack Herrington. Code Generation in Action. Manning, 2003. ISBN: 1930110979. http://www.codegeneration.net/cgia/

Xactium. Applied Metamodelling: A Foundation for Language Driven Development. 2004.

http://albini.xactium.com/content/index.php?option=com_remository&Itemid=28

Other Resources on the Web:

[3] Matt Quail. Totally Gridbag.

http://madbean.com/blog/2004/17/

Jack Herrington. Code Generation Network.

http://www.codegeneration.net/

[4] Intentional Software

http://www.intentsoft.com

[5] Xactium

http://www.xactium.com

Intentional Programming interviews

Sergey Dmitriev.

http://codegeneration.net/tiki-read_article.php?articleId=60

Charles Symonyi.

http://codegeneration.net/tiki-read_article.php?articleId=61

Krzystof Czarnecki.

http://codegeneration.net/tiki-read_article.php?articleId=64

Andy Evans.

http://codegeneration.net/tiki-read_article.php?articleId=68

See Also:

《Thinking in Current Paradigms, Platforms, Frameworks, and Libraries 》

《Thinking in Current Programming Languages 》