Preprocessor
预处理器的分类
词法预处理器:在词法分析的基础上进行低级的预处理,按用户定义的规则对记号流进行简单的替换。词法预处理器典型地执行宏展开、文件包含、条件编译等。例如,C预处理器、m4、诸如autoconf的跨平台build系统、GEMA等。
语法预处理器:按用户定义的规则,在语法树上进行变换。对于一些语言,如Lisp和OCaml,规则用同一语言编写,这称为编译期反射(compile-time reflection)。另外有一些语言依靠外部语言来定义要进行的变换,例如用于XML的XSLT预处理器。
语法预处理器典型地用于:
- 定制语言的语法,如Objective Caml中有两种不同的语法,normal syntax、revised syntax
- 通过增加新的原语扩展语言,如Lisp通过宏扩展语言;像Scheme或Common Lisp这样的语言(有简单的动态定型核心),其标准发行本允许使用命令式或面向对象编程以及静态定型,这些特征绝大多数都是通过语法预处理来实现的。
- 在通用编程语言中嵌入领域专用语言(DSL, domain-specific language),如MetaOCaml
通用预处理器:指不局限于一种编程语言或一种特定的用法。M4是著名的通用预处理器。
Macro
普通宏和Hygienic Macro的区别:
- 若宏调用处有个名字name1,同时宏内部也有一个name1,hygienic宏在展开时会把自己内部的name1改名成name2,普通宏则不改名。
[TR1986]提出了一种解决Lisp中宏函数展开的方法。 [ESOP2008]给出了Hygienic宏的理论。
Object-oriented Features
Inheritance
继承的目的是为了抽象和复用代码。
Class-based Inheritance
类继承是通过定义对象的类来继承,以复用类中的数据成员和方法成员。
支持类继承的语言有: Simula(引入class)、Smalltalk(首个规范的基于类的语言)、PHP、C++、Java、C#、Objective-C等。
单一继承
多重继承
- 规格继承:继承一堆方法接口的集合,如Java
- 实现继承:除了方法的接口还允许有方法的实现,如C++
- 基于Mixin[OOPSLA1990]的继承: 一个类可以使用其他类中的方法,如Ada、D、Groovy、JavaScript Delegation - Functions as Roles (Traits and Mixins)、Perl6、Python、Ruby、Scala、Vala、Swift等
多重继承的缺点
- 结构复杂化:如果是单一继承,一个类的父类是什么,父类的父类是什么,都很明确,因为只有单一的继承关系,然而如果是多重继承的话,一个类有多个父类,这些父类又有自己的父类,那么类之间的关系就很复杂了。
- 优先顺序模糊:假如A,C类同时继承了基类,B类继承了A类,然后D类又同时继承了B和C类,所以D类继承父类的方法的顺序应该是D、B、A、C还是D、B、C、A,或者是其他的顺序,很不明确。
- 功能冲突:因为多重继承有多个父类,所以当不同的父类中有相同的方法是就会产生冲突。如果B类和C类同时又有相同的方法时,D继承的是哪个方法就不明确了,因为存在两种可能性。
Prototypal Inheritance
原型继承是通过原型来对现存的对象进行复用,即一个对象可以基于另一个对象来创建,复用另一个对象的实现。在实现上,每个对象都有一个指向其原型对象的内部链接。
支持原型编程的语言有:Common Lisp、JavaScript、Lua、Perl with the Class::Prototyped module、Python with prototype.py、R with the proto package、Self等。