编程知识框架图(元编程语言的诞生)(1)

2022年9月,深度社区正式公开了自研通用目的编程语言——Unilang!

这一门新兴诞生的、以实现根技术为目标的元编程语言,能够适应更有效和灵活开发桌面环境应用,统筹解决新方案中语言部分的不足。

本篇文章中,我们将尽可能以深入浅出的方式,带各位一同理解它的模块化高可复用性高可拓展性这几大特性。

模块化

这里的模块化和一般开发者理解的不同,是体现在语言设计自身上的;或者说,不是写程序代码,是写语言规范上需要手动修改考虑的东西。

比如 :C 有 n 个特性,而每提议加一个新特性,可能就得考虑和现有的 n 个特性组合的交互——分析有没有矛盾,如果有问题如何调整设计等等,这使得语言的设计复杂度变成了 O(n^2) 。而且这些工作都得对照文档,由人工完成,这就是比较“不模块化”的、低效的一种表现。

因为历史原因,C 在主流语言中时常表现不佳。近年来 C 确有不少特性因为无法取得共识而在临近新版本发布前被剔除,并影响整个进度的情况。而一开始就不够模块化的非正交设计导致的复杂性,正是阻碍取得共识的一个重要原因。

这种模块化以往通常被忽视(或至少仍然不够被重视),因为大部分开发者通常不考虑怎么改进语言,也不易认识到模块化会是明显的优势。由于本文篇幅有限,对unilang的模块化设计不便展开,后续我们也将找机会进行扩展描述。

高可复用性

Unilang强调一等对象 (first-class object) ,这就使得几乎任何源程序组件都更容易复用——只要语言的用户愿意的话。

一些现代语言(如Scheme 、Rust 等)支持干净的过程宏(hygienic procedure macro) ——以类似函数的语法进行宏的调用,而较为传统的非卫生宏往往是基于文本或者记号串展开的,容易造成环境污染和导致可维护性缺陷。不过,过程宏相对“真正”的函数(过程)也有一些限制。例如,宏不是一等对象,无法作为参数传递。此外,实现宏的语法和实现函数体的语法类似,但并非完全一致,一般需要支持特设的模板语言,和函数体使用的语法不完全通用。

相对地,Unilang 没有提供过程宏,但也能完全具备过程宏的优势。代替过程宏的是 Unilang 中更一般的函数;只是和其它的函数不同,它们不求值调用的参数。作为一等对象,函数可以作为其它函数中的实际参数或从函数返回。同时,不需要为了支持过程宏而特设语法,更易学易用。

更重要地,Unilang 函数调用不一定需要像宏展开一样,必须提前在其它代码的运行前被预处理,这相当于为新增运行时展开宏的能力提供了更强大的跨翻译阶段抽象,减少了语言设计与程序开发的冗余。

特别的是,它们可以用于实现编译器。只要实现者愿意,这些逻辑就可能作为可调用的代码片段直接使用,且它们的实现代码不因运行阶段而需要调整。而在传统的语言实现中,即使编译器的作者愿意,想要运行时调用编译器的功能,通常得通过一套 JIT API ,并且其实现和 AOT 编译器经常相当地不同,难以复用。

高可拓展性

Unilang 的基础语言和语言扩展的底层设计,使开发者能有机会以前所未有的方式、平滑地改进现有语言的设计和实现,并使它们保持兼容——而非等待和语言设计及实现者去做沟通或反馈。

当前 Unilang 的设计中提供基础语言和上层语言特性。后者主要就是通过前者实现的库;用户能够使用 Unilang 程序继续向现有语言中通过库的形式追加提供新的特性,或者对下游开发者屏蔽一些特性。

和现有语言的一项关键差异是,Unilang 通过库可以实现更广泛的特性。例如,Unilang 不需要内建如模块管理的支持;不需要提供创建函数的特设语法,而通过函数构造器创建函数实现——如 lambda 这样的关键字,在 Unilang 中就是个普通的库函数(事实上,lambda 甚至不需要被原生实现,因为可通过其它更基本的函数派生)。

另外,在平滑改进语言方面,Unilang 提供了一种和 LOP (面向语言编程)部分等效的新路线——语言派生(language derivation),语言的扩展得到的结果默认仍然和 Unilang 语言兼容。这种改进能解决复用代码时需要较大的附加学习和部署成本的问题。

需要说明的是,C 扩展到 C 、C 扩展到 C /CLI 这些传统的例子也可被视为语言的派生。但这要求手动扩展和维护新的语言规范,原则上是不可编程的。而Unilang 的方式更强调可编程和兼容性,而非创建新的语言(尽管不限制这样做),所以不需要这些工作。

当然,并非所有开发场景都能发挥这些优势,但这不影响语言其余部分的可用性。对普通的应用开发场景,可以直接使用上层语言特性。而对根方案有定制需求的开发者,可以选用适合自身场景的特性集自行扩展。

但是要完全发挥出这里的优势有个很现实的工程问题:开发底层特性的人手严重不足。毕竟从功能上来说,不管写成库还是直接放在语言里,维护人员都需要准确理解需求设计出接口。所以短期内,仍优先考虑满足桌面开发的解决方案。

全局特性设计

除了设计之初所强调的三大特性之外,Unilang的全局特性设计也非常值得一提:

1、得力于显式求值和一等环境的设计,语言具有同像性(homoiconicity) 。语言原生不提供显式类型系统,而使用潜在类型(latent typing) ,这些设计能提供相对很多语言更强的语言层次上的可扩展性——例如,允许用户自行添加和扩展类型系统。

2、不要求全局 GC ,同时语言的一个子集允许和 C 同等层次的“不安全”,但能确保确定性的资源分配。

3、支持 PTC(proper tail call) ,用户程序就极少会遇到栈溢出的问题,从而不用处理这种令人头痛的未定义行为了(比如特意用CPS风格改写程序或者在程序里显式检查栈溢出)。

4、和 C 具有良好的互操作性

了解更多

初步学习和使用 Unilang:https://github.com/linuxdeepin/unilang/blob/master/doc/Introduction.zh-CN.md进一步了解 Unilang 语言规范:https://github.com/linuxdeepin/unilang/blob/master/doc/Language.zh-CN.md了解更多 Unilang 已实现的功能特性:https://github.com/linuxdeepin/unilang/blob/master/doc/Features.zh-CN.md

点击阅读原文,访问项目地址,期待你的到来,帮助Unilang一同成长!

,