编程语言:从历史到应用,如何成为平台的创作工具?
编程语言平台的创作工具在规划新项目并选择编程语言时人们常陷入两种误区。要么尊崇“学术谱系”如函数式范式或类型系统的纯粹性要么盲目追随“社区趋势”受 GitHub 星标数量和程序员社区热议影响。认为优雅数学证明或时髦语法能保证产品成功虽天真但可理解毕竟人们希望所学编程语言及其范式经得住时间考验。实际上每个程序执行最终归结为机器指令和状态转换。即便原始程序可能不完全转换为机器码若仅关注计算能力高级语言并非必需同样计算可用机器码或汇编语言表达。然而实际发展并非如此大量高级语言和运行时环境应运而生以解决不同问题、适配不同平台。新硬件、操作系统出现或浏览器和智能手机普及适配的编程语言和工具也会跟进。这是因为关键问题已从机器“能否”计算转变为人们“能”在机器上构建多少程序。从这一角度看IBM 让 PC 平台领导权给微软就很好理解。IBM 创建 PC 硬件标准但基于该标准的应用软件和开发者生态与 MS - DOS 和 Windows API 紧密相连。随着兼容 PC 普及硬件成商品控制开发者编程接口的微软成为平台真正主人。只有有游戏、生产力应用程序和内容积累平台才成为用户愿意停留的世界而编程语言就是构建这个世界的工具。程序员用编程语言施加逻辑和规则时就不再只是机器操作员。ELIZA 的创造者约瑟夫·魏泽鲍姆说“计算机程序员是宇宙的创造者而他自己就是这个宇宙的立法者。”不过程序员能用编程语言构建世界不能单独解释语言的成功。表达能力强的语言可能局限于小研究社区饱受批评的语言可能因庞大运行时环境成为行业标准。关于编程语言的讨论常从学术分类开始如类型系统、语义、范式和实现策略等但仅靠这些无法解释其实际发展历程。从就业市场角度能更好回答人们为何学习一种编程语言这也是撰写本文的原因。解读编程语言历史至少需区分三个问题为何被创造、开发者为何采用、为何能长期保持主导地位。为研究设计的语言可能因与工业平台结合成功为特定平台设计的语言可能突破平台边界。仅用语言最初动机解释后续发展会遇问题。平台与互补产品操作系统、浏览器、游戏机或智能手机的价值随运行程序增加而增长。用户接触的是最终产品如生产力应用程序和游戏一台无游戏的游戏机只是昂贵家具。经济学中一起使用能实现价值最大化的产品叫互补产品平台和应用程序就是这种关系。若把基础设施比作空舞台应用程序和内容就是演员和剧本。平台的真正商业价值和市场主导地位取决于其生态系统中互补产品总量而非自身计算能力。对平台运营商来说开发工具类似工厂设备。即使销售编译器不是核心收入来源开发者能更廉价、快速构建程序平台上互补产品增加平台价值也提升。“让你的互补产品商品化”策略适用于编程语言和 SDK。所以“高级语言是为销售编译器而创造”的观点片面企业收入更多来自计算机、操作系统等编程语言是人们持续使用这些产品的理由。编程语言降低的成本编程语言降低的成本远不止减少打字量。说“可用汇编语言实现”是关于图灵完备性即计算表达能力的回答但开发时间、可修改性和移植性等是关于开发成本和实用性的问题。原则上图灵完备语言可表达相同计算但不同语言编写、验证和维护计算的成本不同。语言设计差异体现在成本和责任在人类、编译器和运行时环境间的分配上。现实中常问的是某件事经济上是否可行所以从计算可能性转向成本分配很重要。从这个角度看编程语言历史是对每个时代系统中最昂贵资源的回应。硬件稀缺昂贵时执行效率是主要限制因素软件规模和开发组织扩张后编写、验证和维护代码的人力成本同样重要。高级语言将部分人类工作转移到编译器等上它既是向机器发指令的符号系统也是分配认知和开发成本的接口可看作萨丕尔 - 沃尔夫假说在编程领域的应用。如果 C 语言可以做到为什么还要用 Python1982 年艾伦·佩利斯在《编程警句》中说“一种不会影响你编程思维方式的语言是不值得学习的。”这句话也可理解为能改变价值观的语言这里的“价值观”指感知程序的习惯编程语言决定了这些。本质上一种语言是权衡和子集的集合“子集”指语言设计者划分的“允许的约束空间”。这就引出问题CPython 用 C 语言实现对性能要求高的扩展模块也常用 C 或 C 编写为何不一开始就用 C 语言编写所有代码从底层实现看有道理但作者操作的世界不同。直接用 C 语言调用库要处理指针等问题而 Python 扩展模块将这些工作转化为可通过 import 访问的函数和对象。例如数值计算内层循环由 C 等处理用户可将数组作为单个值传递等。Python 不是 C 语言的替代品而是在不同粒度级别使用原生代码的方式。编程语言的世界观范式可总结如下语言决定的方面更接近 C 语言的层面更接近 Python 的层面基本工作单元地址、缓冲区、结构体、函数调用对象、可迭代对象、模块、可调用对象资源和生命周期显式分配/释放、所有权约定对象生命周期和上下文管理器错误传播返回值、errno、临时约定异常和回溯信息组合模型头文件、ABI、链接、函数指针import、协议、高阶函数探索风格先构建再运行和调试器REPL、自省、笔记本在编程社区C 语言因历史悠久、接近机器而受尊崇但程序员认知资源有限需分配资源。若专注数值计算关注底层控制有意义若需建模数据流和业务规则可将内存管理等委托给“黑匣子”。选择编程语言是在认知资源限制内选择高效工作方式。在同一系统中内层接近机器外层便于修改。C 语言处理计算成本和内存布局Python 处理实验和组装这种分工持续是因为两种语言处理不同变化率。当然边界处有成本但抽象将实现成本集中到少数扩展模块作者身上让更多用户能用自己问题领域的词汇编写代码。学习新编程语言不只是替换语法符号不同语言带来不同思维方式。如果新语言只是更改分号却表达相同思想就没必要学若作者感知的问题和表达语句变化就是不同的创作工具。所以语言选择应由想做的事情驱动选择语言就进入其世界观。从平台经济学角度重读语言历史以 Fortran 为起点之前有先驱语言但这不是争夺“第一种高级语言”头衔的竞赛。讨论编程语言时程序员常提及已消亡语言的谱系但本文关注的是语言如何锁定开发者并成为平台背后的力量。从 1957 年 Fortran 报告开始能看到编程语言、编译器、商用计算机和用户市场的交汇。一种语言首次实现年份与权威文档出版年份不一定一致。关于 Smalltalk、C 等语言的历史论文是设计者多年后的回顾而 Fortran 报告等更接近当时文档。大学和委员会诞生的语言有规范而 C 等语言是从底层发展起来成功后才撰写回顾文章。先有谱系的语言和先赚钱后买谱系的语言区别如下时期语言初始环境本文研究文档的特点1957 年FortranIBM 704同期发布的宣布编译器和语言的报告1960 年COBOL政府和企业的异构数据处理CODASYL 初始规范1960 年LISP麻省理工学院人工智能小组、IBM 704麦卡锡McCarthy的原始论文1960 年ALGOL 60国际算法表示法同期的语言报告1964 年BASIC达特茅斯分时系统1964 年的原始官方手册1962 - 1967 年Simula I · Simula 67离散事件模拟1966 年 Simula I 介绍性论文及后续 Simula 67 的谱系1972 年后Smalltalk施乐帕洛阿尔托研究中心Xerox PARC的个人计算1993 年设计者的回顾文章20 世纪 70 年代C · ML · PrologUnix · LCF · 自然语言处理当代论文和 HOPL 回顾文章的混合1979 年后CC 系统生态系统1993 年设计者的回顾文章1989 年后PythonUnix/C 扩展和快速脚本编写1991 年的第一篇论文1995 年的规范1995 年后Java · JavaScriptJVM · 网页浏览器早期的白皮书和后来的 HOPL 回顾文章2000 年后C#.NET 和 CLI2001 年的第一份 ECMA 标准2015 年及以后Rust 平台采用Android、Windows 和 Linux官方平台文档和安全策略报告1957 年Fortran —— 让我们来销售编译器1957 年巴克斯和他的 IBM 团队发表《FORTRAN 自动编码系统》论文核心不仅是语言语法还有运行在 IBM 704 上的自动编码系统。当时用户担心编译器生成的代码不如熟练程序员手工编写的快若花机器时间节省编程精力却得到慢代码交易就不划算。所以早期 Fortran 团队要在减少编写时间的同时生成高效机器代码用编译器优化弥补语言抽象成本。高级语言在工业领域首次应用并非仅基于“代码可读性”而是能减少程序员时间不浪费 IBM 704 计算能力。1960 年同一年的三种不同答案1960 年诞生的 COBOL、LISP 和 ALGOL 60 对为何需要高级语言给出不同答案。COBOL 初始规范标题含“通用商业导向语言”是共享语言让处理组织记录的程序可在不同制造商计算机间移植。记录等成为关注焦点所以采用“英语句子”形式方便利益相关者阅读和验证。这是 CODASYL 委员会协商妥协的成果汇聚政府和行业代表。COBOL 将组织的账本和程序移植过来用于构建资本主义循环系统从银行支付网络到零售库存处理。“平台”含义从单一机器转向组织的数据处理基础设施企业用 COBOL 可摆脱硬件供应商依赖投资能迁移到下一代机器。21 世纪 20 年代全球银行高价聘请退休人员维持 COBOL 系统运行。同年麦卡锡的 LISP 论文解决不同问题。麻省理工学院人工智能小组希望在 IBM 704 上表示声明和指令并推理实验符号和列表成为主要数据论文统一了递归函数等到单一计算系统。LISP 平台不只是 IBM 704研究人员创建以符号操作为中心的新计算世界。代码和数据用相同列表表示为语言作为内部对象开辟道路后来 Lisp 机器和环境将语言等捆绑。ALGOL 60 报告严格区分语言的参考、硬件和发布表示不与特定供应商编译器绑定。之前编译器定义语言身份之后规范成为权威。商业上ALGOL 未像 Fortran 或 COBOL 那样占据主导但通过硬编码结构约束成为“概念平台”其“基因”延续到后续语言。1964 年BASIC 与响应时间一同设计1964 年达特茅斯 BASIC 手册表明是“为达特茅斯分时系统设计的初等代数语言”脱离背景研究 BASIC 易忽略语法形成原因。分时系统中学生在终端输入程序能快速得到结果。Fortran 降低专业程序员安排工作成本BASIC 让无计算机科学背景学生直接使用计算设备可交互式编辑和重新运行程序。创作工具是 BASIC 语法、分时操作系统和终端的整体体验。早期微软最早理解“即时创作体验”的商业力量1975 年比尔·盖茨和保罗·艾伦将 BASIC 解释器移植到 Altair 8800 并创立公司。对他们来说BASIC 是掩盖碎片化硬件的抽象层。微软将 BASIC 烧录到早期 PC 的 ROM 中硬件制造商被锁定在微软语言生态系统。起源于达特茅斯的理念延续到 Visual Basic实现软件生产力民主化但也让一些开发者为 VB6 代码修改苦恼。带有 BASIC 解释器的机器开机显示编程界面用户安装 SDK 前就能创作。1962 - 1972 年开始创作世界而非仅仅是程序Simula 名字最早在 1962 年文档出现1966 年达尔和尼加德介绍 Simula I它是 ALGOL 60 的扩展用于描述离散事件系统。虽不能将 Simula 67 所有概念追溯到该论文但组织系统内活动和事件的方向明确。Simula 67 定义类、对象和继承后程序成为组件交互的世界模型。Smalltalk 将此概念扩展到个人计算环境。艾伦·凯追溯其起源于分时计算机等研究。1972 年 Smalltalk 不仅是语法还是连接对象、消息等的媒介。如今语言和 IDE 常被视为独立产品但 Smalltalk 中编写、运行、检查和修改代码的环境在一个系统中用户重塑实时计算媒介是“编程语言是平台的创作工具”的贴切例子。20 世纪 70 年代嵌入其他工具的语言ML 一开始不是针对独立通用语言市场而是作为在爱丁堡 LCF 中编写定理证明策略的元语言。LCF 通过可信内核和定理抽象保留证明对象逻辑完整性ML 的多态类型推断和抽象类型能捕获策略代码类型错误限制内部表示访问。米尔纳 1978 年论文解释了类型规则和算法 W 在 ML 中的实现。宿主平台是定理证明器ML 是扩展 LCF 的安全创作语言后来分支发展出标准 ML 等。这表明编程语言历史不仅是“占领主导平台”的叙事也是副产品的历史。Prolog 起源于法国自然语言处理项目1971 年末有初步版本1972 年末有更成熟系统。描述逻辑关系后让解释器搜索答案的方法源于实际需求。LISP、ML 和 Prolog 难以用单一硬件销售策略解释人工智能研究等工作环境需要这些语言而它们又扩展了研究环境的实验范围。创作工具由控制特定平台的愿望塑造。20 世纪 70 年代之后C 语言和 Unix 构建的兼容性帝国Multics 项目结束后回归小型系统20 世纪 60 年代末贝尔实验室退出 Multics 项目研究人员觉得等 Multics 可用时太晚且成本高但从 Multics 学到的理念得以延续。以肯·汤普森为核心的团队在小机器上重新创建可控制的环境。当时 Unix 不是官方项目1969 年汤普森等人多次请求购买 PDP - 10 或 Sigma 7 未获批。汤普森将 Space Travel 游戏移植到 PDP - 7 上这款游戏让他熟悉 PDP - 7 并开始文件系统和操作系统实验。PDP - 7 上第一个系统用汇编语言编写早期程序在 GECOS 系统构建后通过纸带传输到 PDP - 7。汇编器完成后Unix 成为自给自足环境文件系统等在实际使用中不断修订小团队能快速测试更改。1970 年获批购买的 PDP - 11 用于文档编辑和排版为贝尔实验室专利部门处理文档为 Unix 带来第一个机构客户。汤普森用 PDP - 7 的交叉汇编器为 PDP - 11 重写 Unix第一个版本接近汇编语言。管道不是 PDP - 7 最初设计部分1972 年道格·麦克罗伊推动添加管道和管道线。密切合作的人共同修改操作系统等每一层可一起更新以适应新连接模型可组合方法成为 Unix 优势。PDP - 11 促使 B 语言向 C 语言发展早期 Unix 的高级语言是 B 语言由汤普森从 BCPL 简化而来。PDP - 7 和 PDP - 11 的 B 编译器生成线程代码执行速度慢且 B 语言只能处理单个机器字在 PDP - 11 上有问题。1971 年里奇在 B 语言中添加 char 类型构建直接生成 PDP - 11 机器指令的编译器。过渡阶段 NB 引入 int 等到 1972 年采用 C 语言名称。里奇指出汤普森创建 B 语言和早期 Unix他在 1971 - 1973 年将 B 语言转变为 C 语言。此时编译器能生成小而快的代码到 1973 年夏天大部分 Unix 内核用 C 语言重写。很难说先有语言还是先有操作系统C 编译器成熟可移植更多 Unix 代码编写内核和工具时的问题反馈到类型和库设计中。PDP - 11 特性、B 编译器局限性和操作系统代码需求相互交织影响 C 语言和 Unix 的形成。这一转变使 Unix 移植到新机器时不必用汇编语言重写整个操作系统准备好 C 编译器后只重写特定于机器的部分。可移植性虽非免费但效果显著大部分操作系统代码基本不变编译器等部分代码共享。C 语言可移植性过程中不同层情况不同层基本保持不变为新机器重新适配语言和源代码控制结构、函数、结构体和大多数应用程序代码整数宽度、对齐方式、字节序和实现定义的行为编译工具链前端和优化器的公共部分代码生成器、汇编器和重定位Unix 接口文件描述符、进程、管道、C 库约定系统调用实现、驱动程序、依赖于机器的代码开发环境shell、make、调试器、文本工具、手册启动、设备管理、打包和部署C 编译器是可移植接口让应用程序不必直接处理新指令集将依赖于机器的代码集中在编译器后端和操作系统狭窄范围减少不同平台上代码重写部分。C 编译器等形成单一工作环境开发者学习 C 语言语法时也吸收 Unix 处理方式。C 语言让 Unix 能在多台机器重生Unix 证明 C 语言不是玩具语言。1983 - 1992 年Unix 兼容性成为新系统的分发渠道GNU 项目于 1983 年 9 月宣布启动1984 年 1 月开始开发。目标是构建全新、免费且与 Unix 兼容的操作系统任何人可使用、修改和分发。选择与 Unix 兼容是因为 Unix 家族设计在多台机器验证现有用户能轻松迁移知识和程序。这里的兼容性不仅是继承设计也是引入现有用户知识和习惯的采用策略。GNU 推出 GCC、GDB 等工具这些工具可在现有 Unix 系统上使用无需等待完整 GNU 操作系统完成。