Language Oriented Programming
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
人们习惯性的认为程序是作为文本来存储的,也就是说,一个字节流;为什么不应该是呢?毕竟有无数的工具来编辑、显示、操作文本;今天的编程语言的核心部分是文法器,解析器,编译器和面向行的调试器;但是程序的文

