元编程技术综述

目录

# 作者简介 #

# 译者按 #

#1 介绍

#2 元编程模型

##2.1 宏系统

##2.2 反射系统

##2.3 元对象协议系统

##2.4 面向切面编程

##2.5 产生式编程

## 2.6 多阶段编程

#3 元程序求值阶段

##3.1 预处理期求值

##3.2 编译期求值

##3.3 执行期求值

#4 元程序源码位置

##4.1 嵌入在主体程序中

##4.2 在主体程序外部

#5 与对象语言的关系

##5.1 元语言与对象语言不区分

##5.2 元语言扩展对象语言

##5.3 元语言与对象语言的区别


# 作者简介 #

Yannis Lilis 博士是欧洲最大的汽车租赁公司的高级程序员,兼职在克里特大学的计算机科学系任客座讲师,在数学及应用数学系任兼职教师,同时兼任计算机科学研究所(ICS)FORTH 实验室的研究员。他的研究兴趣包括软件工程、机器学习、计算机视觉、人工智能、分布式系统等等。他总计发表了 20 篇会议 / 期刊等出版物,其中超过半数为元编程相关技术文章。

Anthony Savidis 教授是克里特大学计算机科学系的正教授和计算机科学研究所(ICS)FORTH 和 HCI 实验室的研究员。他的研究兴趣包括:编程语言、软件工程、用户界面开发工具、特定领域工具以及面向学习者的交互式编程环境。他参与了 35 项欧洲和国家研发项目,发表了 150 多篇会议 / 期刊 / 书籍出版物(2600 次引用,hIndex 30),曾任信息通信技术中心主任,现任克里特大学数据处理实验室主任。他在用户界面管理系统、脚本编程语言、自适应界面、下一代 IDE 和调试器、可访问的用户界面工具包方面做了开创性的工作,并在 2003 年开发了最早的物联网软件工具包之一。

个人主页:https://www.ics.forth.gr/person/Savidis/Anthony


# 译者按 #

元编程是计算机编程中一个非常重要、有趣的概念,可以很大程度地简化和复用代码。元编程几乎是现代编程语言的必备能力之一,在数值计算、编译器设计和实现(类型推导、代码生成……)、领域专用语言(DSL/eDSL)设计、框架开发等领域有广泛的应用。

本文《A Survey of Metaprogramming Languages》是一篇系统介绍元编程技术的综述类文献,比较全面地对已有的元编程模型做了分类,并且对各类元编程的特点做了介绍。让读者可以清晰地了解元编程的历史、编程范式、代表语言…… 这也是译者选择这篇文献的初衷。近几年,在系统编程领域大热的 Rust、发力科学计算领域 Julia、擅长 Web 应用的 Ruby,都非常依赖和推崇元编程技术。遗憾的是,本文没有对这几门语言中的元编程做分析归类。另,原文献第二章中除对概念的介绍外,列举了众多语言的元编程系统作为示例解读,由于篇幅原因,译者只摘选了针对主流语言的介绍,省略部分语言,感兴趣的读者可阅读原文了解更多。

由于文献的性质,本文只能算是对元编程的意况大旨,欢迎读者朋友加入我们的编程语言技术社区 SIG-元编程小组,和我们一起对元编程技术做更深入的探讨。(加入方式:文末有小助手微信,添加并备注加入SIG-元编程)

原论文地址:https://dl.acm.org/doi/epdf/10.1145/3354584

#1 介绍

元编程是一种将计算机程序当作数据进行处理,从而生成新程序或修改现有程序的技术。元程序所用的语言,我们称之为元语言,生成或转换成的程序所使用的语言,我们称为对象语言 [1]。如果对象语言和元语言相同我们称为同构元编程,若不同,则称为异构元编程。元编程技术在生成或修改代码时,需要将代码在抽象层面进行表示,一般采用抽象语法树(AST,Abstract Syntax Tree,起源于 Lisp S 表达式 [2])的形式。为了让开发者更便捷的操作 AST,不同的语言有不同的形式,本质来说均是通过元语言编写元程序,修改目标代码的 AST。

运用元编程技术带来的收益 [3] 主要体现在以下三个方面:

提升程序性能:开发者根据特殊的应用场景(specializations),结合自己的领域知识,编写元程序自动生成对应的高效程序,而不是利用通用但低效的程序。另外,程序特化(partial evaluation)可以通过识别静态输入(编译期可知的输入)并在编译期进行求值从而最小化运行时开销。

提升程序的理解力:元编程技术可以分析对象程序特性,从而可以进一步对其进行优化,检查和验证(例如流分析器和类型检查器等)。

提升程序复用能力:这是元编程技术最大的优点。编程语言可以通过函数,泛型,多态,类和接口等支持代码复用,但是有一些重复出现的代码模式无法借助以上技术实现复用。而元编程可以对代码片段进行操作,转换或生成代码,因此可以借助结构化的表示描述那些模式,并作为独立的,可重复使用的单元进行发布。

元编程并不是一个新概念,其历史十分悠久,最早可追溯到 Lisp 的宏,图 1 展示了不同语言元编程系统的发展时间线。近年来,随着新的元编程范式的出现,如切面编程 aspect-oriented programming [4]、多阶段编程 multistage programming [5]、产生式编程 generative programming [6] 等,元编程领域受到越来越多人的关注。

图片

图1:元编程相关语言及系统发展时间线,表格可能不详尽

参考:

1.    元语言和对象语言的概念来源于逻辑和语言学,见:https://en.wikipedia.org/wiki/Metalanguage

2.    Guy L. Steele. 1990. Common Lisp: The Language (2nd ed.). Digital Press.

3.    Tim Sheard. 2001. Accomplishments and research challenges in metaprogramming. In Proceedings of the 2nd International Workshop on Semantics, Application and Implementation of Program Generation (SAIG’01). Springer LNCS 2196, 2–44. DOI:http://dx.doi.org/10.1007/3-540-44806-3_2

4.    Gregor Kiczales, John Lamping, Anurag Mendhekar, Chris Maeda, Cristina Lopes, Jean-Marc Loingtier, and John Irwin. 1997. Aspect-oriented programming. In Proceedings of the 11th European Conference on Object-Oriented Programming (ECOOP’97). Springer LNCS 1241, 220–242. DOI:http://dx.doi.org/10.1007/BFb0053381

5.    Walid Taha and Tim Sheard. 1997. Multi-stage programming with explicit annotations. In Proceedings of the Symposium on Partial Evaluation and Semantic-Based Program Manipulation (PEPM’97), ACM, New York, 203–217. DOI:http://doi.acm. org/10.1145/258994.259019

6.    Krzysztof Czarnecki and Ulrich W. Eisenecker. 2000. Generative Programming: Methods, Tools, and Applications. ACM Press/Addison-Wesley Publishing, New York.

#2 元编程模型

元编程采用的模型主要有以下几类:

  1. 宏系统(macro system)

  2. 反射系统(reflection system)

  3. 元对象协议系统(metaobject protocols)

  4. 切面编程(aspect-oriented programming)

  5. 产生式编程(generative programming)

  6. 多阶段编程(multistage programming)等

每种模型可以解决不同的工程问题,同时也存在密切的联系,跨模型的交叉应用能解决更具挑战的问题。

图片

图 2:元编程的四个分类维度以及其各自的子类(含章节序号)

##2.1 宏系统

模型解释

是一个重要的元编程模型,其类似于函数的功能,完成某种输入到输出的映射。对于宏来说,目标代码片段作为输入序列,输出序列为新的代码片段。这个映射过程称为宏展开,会一直展开直到目标代码中没有宏为止,宏展开后的目标代码继续执行正常的编译流程,如图 3 所示。宏系统可以分为两大类:词法宏语法宏词法宏作用范围在标记序列上(sequence of tokens),如 C/C++ 的宏;语法宏能获取语法,语义信息,可以认为其作用在抽象语法树(AST)上。宏系统可以是过程式的(Rust 过程宏),即对输入进行系列计算从而得到输出;也可以基于模式匹配及替换(Rust 声明宏)完成代码变换。最后,宏系统一个重要的属性是卫生扩展(hygienic expansion),即保证没有由宏扩展引入的意外的由宏扩展引入的名称冲突,这个问题通常被称为变量捕获

图片

图 3:宏系统:宏调用在迭代宏展开的过程中更新程序表达,最终使用无宏调用的程序完成翻译

代表语言和系统

CPP(C preprocessor)[1] 是词法宏的典型代表。词法宏一般与语言的翻译器(编译器或解释器)是解耦的,有特殊的预处理器(如 C 语言预处理器)将目标代码中的宏进行预处理展开,然后进行翻译。这类宏只能处理比较简单的文本替换问题,且容易产生副作用,如命名冲突等。

第一个使用基于语义的宏系统的编程语言是 Lisp。在 Lisp 中,代码即数据,任何可以对数据结构进行的操作,Lisp 的宏均可以对代码进行类似操作。如下图所示,还有很多编程语言有各具特色的宏系统。

表 1:宏系统分类

图片

参考:

  1. Brian W. Kernighan and Dennis M. Ritchie. 1988. The C Programming Language (2nd ed.). Prentice-Hall, Englewood Cliffs, NJ.

##2.2 反射系统

模型解释

反射是指在运行时计算机程序可以访问,检测和修改它本身状态或行为的一种能力 [1]。反射的两个主要概念是内省(introspection)调整(intercession)。内省是程序检查其自我表示的能力,而调整是程序修改其自我表示的能力。另一个区别是结构反射和行为反射之间的区别。结构反射是程序访问其结构表示的能力(例如,类、方法和字段),而行为反射是它能够访问其切面的动态表示(例如,变量赋值、对象创建、方法调用等)。这两个区别是正交的;内省和调整与访问的类型有关(即,只读或读 / 写),而结构与行为的区别与自身的类型有关。

代表语言和系统

在面向对象的编程语言中,反射允许在编译期间不知道接口、字段、方法的名称的情况下在运行时检查类,接口,字段和方法。它还允许根据判断结果进行实例化新对象和不同方法的调用。反射还可以使给定的程序动态地适应不同的运行情况。例如,考虑一个应用程序,它使用 2 个不同的类 X 和 Y 互相交替执行类似的操作。没有使用面向反射编程技术,应用程序可能是硬编码的(即把代码写死,缺乏灵活性),以调用方法名称的类 X 和 Y 类。然而,使用面向反射的编程范式中,应用程序可以在设计和编写利用反射在没有硬编码方法名称情况下调用类中的方法 X 和 Y。如下图介绍了 Java 反射系统。

图片

图 4:Java 的动态代码生成 (上) 和反射(下)

虽然 Java 的反射是在运行时实现的,但反射不局限于此阶段。在编译期反射中,元程序通过反射获取的相应字段和方法信息,生成特定的目标代码。这样的反射系统主要聚焦于代码生成,也属于产生式编程的元编程模型,会在后续继续讨论。

如下图所示,还有很多语言支持反射系统。

表 2:反射系统相关的语言

图片

Symbol ↓ means there is limited support for the feature.

参考:

  1. Pattie Maes. 1987. Concepts and experiments in computational reflection. In Conference Proceedings on Object-Oriented Programming Systems, Languages and Applications (OOPSLA’87). ACM, New York, 147–155. DOI:http://dx.doi.org/10. 1145/38765.38821

##2.3 元对象协议系统

模型解释

元对象协议(MOPs,Metaobject Protocols)[1] 最初是一种能逐步改变原始语言行为和实现的接口(interface),最具代表性的例子有 Smalltalk 的 MOPs 和 Common Lisp 的对象系统。用通俗的话来说,MOP 能够访问对象系统中对象的结构和行为。在这个模型中,普通的类(class)被认为是元类(metaclass)的对象(object,或者说实例),称为元对象( metaobjects )元类(metaclass)负责整个对象系统并提供 API 修改其行为(例如,通过继承不同的类来创建新方法,修改现有的方法,修改类的结构等),如图 5 所示。从这个角度看,所有在 meta-level 对元对象的修改,都会直接影响 base-level 继承自这些元对象的普通对象。

图片

图 5:使用元类和元对象协议改变对象系统行为

上述内容是基于元类方法的

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值