编程语言的慢演进:从 1960 年代到 AI 时代的语言进化悖论
横看成岭侧成峰,远近高低各不同。不识庐山真面目,只缘身在此山中。 —— 苏轼《题西林壁》
身处编程语言的演进之中,我们往往被语言表面的新特性迷惑,而看不清历史的脉络。真正的洞察需要跳出当下的视角,从历史的维度审视语言的本质。所谓创新,多是对过去的重新发现;所谓进步,常是在既定轨道上的缓慢前行。
有个挺有意思的现象:技术圈天天喊着 AI 革命、量子计算、元宇宙,但你看看大家每天用的编程语言,前十名基本都是上个世纪的「老古董」。这是为啥?
最近看了 Kevlin Henney 在 GOTO 2024 的演讲「Programming Languages: Past, Present, and Future」(https://www.youtube.com/watch?v=0y7IFUYUTgg),他用数据和历史告诉我们答案。更扎心的是,他说 AI 时代不但不会让编程语言更新换代,反而会让老语言更加根深蒂固。
先说个简单但很多人忽略的事实:编程语言不是什么细枝末节,它是一切的根基。
编程语言:软件世界的「地心引力」
Kevlin 打了个很妙的比方:NASA 那个 160 米高的火箭装配大楼,看起来很宏伟,但它的形状完全取决于两个看不见的东西 —— 材料和重力。换了任何一个,整栋楼就得重新设计。
编程语言就是软件世界的「重力」。它无处不在,决定着你怎么思考问题、怎么设计系统、甚至团队怎么协作。不信?让你们团队明天换个语言试试 —— 这可不是换个工具那么简单,整个技术栈、思维习惯、开发流程全得推倒重来。
Kevlin 说得好:「如果编程语言真的无关紧要,你咋不明天就换一个?」
历史的重量:1960 年代的语言革命
让我们回到 1960 年代,那个看似遥远却影响深远的年代。
ALGOL 68:被遗忘的巨人
你可能压根没听说过 ALGOL 68,更别提用它写过代码了。但有意思的是,你每天敲的那些关键词 —— int、char、void、struct、union —— 全都是从这门语言来的。在它之前,编程语言都老老实实用完整的单词:integer、character、boolean。
ALGOL 68 可不光是把关键词变短了这么简单,它还搞出了表达式导向的编程风格。没有 return 语句,所有东西都是表达式。这个设计思路后来被函数式编程语言玩得飞起。
if-else 的发明:一个看似简单的创新
听起来有点扯,但 if-else 这玩意儿还真是需要被「发明」出来的。英语里大家都说「if something, otherwise」,谁会说「if something, else」?早期搞编程语言的大佬们为这事儿可没少纠结:
- Grace Hopper 在 FLOW-MATIC 里用的是
OTHERWISE - John McCarthy 早期设计 Lisp 时也觉得
otherwise比较顺口 - ALGOL 58 的提案里还有
or if和if either这些奇葩选项
一直到 1960 年,ALGOL 60 委员会才拍板用 else。Klaus Samelson 这哥们儿是关键人物,他被 McCarthy 的想法打动了,力推这个简洁的关键词。
协程:60 年前的「新」概念
现在的程序员一提到 async/await、goroutines、协程这些东西就激动得不行,但你知道吗?协程(coroutines)这个概念是 1958 年就有的。没错,1958 年,那时候很多读者的父辈们可能都还没出生呢。
Melvin Conway(对,就是那个搞出 Conway’s Law 的大神)在 1958 年就琢磨出了协程这个概念。Donald Knuth 在 60 年代初期还专门深入研究了一番:「主程序和子程序之间是老大和小弟的关系,但协程之间完全是平等的哥们儿。」
// 现代 Rust 中的协程风格
async fn fibonacci_generator() -> impl Stream<Item = u64> {
let (mut a, mut b) = (0, 1);
stream! {
loop {
yield a; // 1970年代的 yield 概念
(a, b) = (b, a + b); // 1963年的多重赋值
}
}
}
Barbara Liskov 在 70 年代搞出了 CLU 语言,里面的迭代器直接就用了 yield 关键词。现在 Python、JavaScript、C# 里的生成器和迭代器,说白了就是在重新发明半个世纪前的轮子。
令人震惊的稳定性:语言也有冻龄现象
来看看 2024 年 9 月的编程语言排名,这数据挺有意思的:
把 TIOBE、RedMonk、IEEE Spectrum 三大排名的 Top 20 综合起来看:
- 80% 的语言都是 2000 年之前造出来的
- Top 5 直接霸占了 55% 的市场份额
- Top 10 更是占了 67% 以上
这就是典型的幂律分布(Power Law),社会结构里到处都是这种模式。更有意思的是按年代来看:
那个「千禧一代」的大饼就钉在那儿了,成了编程语言历史上永远的一个山头。这意味着啥?大部分刚毕业的小年轻,用的编程语言比他们岁数都大。
社会性超越技术性:语言成功的真正密码
重新想想这个问题:为啥有些语言能一直火下去?答案可能会让技术原教旨主义者很失望:编程语言能不能成功,主要看社会因素,技术牛不牛逼反而是次要的。
Java:「无趣」即是特性
有次聊天时,有人说「Java 作为一种语言挺有趣的」,Kevlin 直接怼回去:「no no no,它很无趣」。但有意思的是,这恰恰是 Java 能成功的原因。
Java 从 1995 年出生那天起就不是一门「有趣」的语言。没有什么革命性的概念,语法也谈不上优雅,Lambda 表达式都拖到 2014 年才加进去(比 C++ 还晚)。但就是这种「无趣」—— 稳定、可预测、向后兼容 —— 让企业最喜欢用它。
语言进化的收敛效应
看看最近几年主流语言的更新,你会发现一个有意思的事儿:大家都在往一个方向跑。几乎所有主流语言都在加这些东西:
- Lambda 表达式和闭包
- 模式匹配
- 类型推断
- 异步编程支持
- 函数式编程特性
// Rust 的模式匹配和类型推断
let result = match value {
Some(x) if x > 0 => x * 2,
Some(x) => x,
None => 0,
};
// 函数式风格的集合操作
let evens: Vec<i32> = (0..100)
.filter(|x| x % 2 == 0)
.collect();
John Backus 1977 年就看出来了:「每个新语言都会把前辈的特性全抄过来,然后再加点新花样。」只不过现在不是造新语言了,而是往老语言里不停地塞新功能。
函数式编程的悖论:永远的明天
1977 年,John Backus 拿图灵奖的时候问了一句:「编程能不能摆脱冯诺依曼那一套?」他说函数式编程就是答案。
47 年过去了,咱们看看函数式编程语言在排行榜上的表现:
三大排名的 Top 20 里,纯函数式编程语言有几个?0 个
没错,一个都没有。Haskell 没进去,Clojure 没进去,F# 也没进去,甚至老祖宗 Lisp(偶尔能混进前 30)都比 Clojure 排名高。
但这不代表函数式编程失败了。恰恰相反,它的思想已经渗透到几乎所有主流语言里了。这就跟太极拳似的 —— 虽然没几个人专门练纯正的太极拳,但太极的理念(以柔克刚、借力打力、阴阳平衡)被各种现代武术和健身方法都借鉴了。这些语言不是函数式的,它们只是偷学了点函数式的「内功」。
Excel:意外的函数式编程王者
Simon Peyton Jones(Haskell 的大佬)说过一句让人扎心的话:「Excel 是世界上最流行的函数式编程语言。」
很多程序员听了都不爽,因为这意味着财务部那帮人可能比他们更早开始「函数式编程」了。想想也对,Excel 的每个单元格都是纯函数,整个表格就是一个巨大的数据流图。没有副作用,没有可变状态,妥妥的声明式编程。
更搞笑的是,2020 年疫情期间,微软的工程师闲得没事干,决定给 Excel 加个 Lambda 函数支持。这就像给一辆已经跑得挺好的车加了个火箭推进器,几乎没人会用,还不如去修修 Excel 那些真正让人头疼的老毛病。
AI 时代:进化的刹车而非油门
这就引出了 Kevlin 演讲里最精辟的观点:AI 和大语言模型火了之后,不但不会加速(给人类使用的)编程语言的进化,反而会变成一股强大的保守势力。
LLM 的统计偏见
大语言模型天生就是保守派,原因很简单:
- 训练数据偏老:老代码比新代码多得多,旧版本的例子比新版本多得多
- 流行的更准:语言越流行,数据越多,AI 给的建议就越靠谱
- 循环加强:AI 推荐主流方案 → 更多人用 → 产生更多数据 → AI 更爱推荐
你让 AI 帮你写代码,它大概率会给你:
- 用老版本语言特性写的代码
- 保守、传统的实现方式
- 基于大量历史案例的「稳妥」方案
碰到小众语言或新特性,AI 立马就不行了,错误率蹭蹭往上涨。这就形成了一个恶性循环,强者愈强,创新愈难。
机器学习算法走进酒吧
Kevlin 讲了个特别妙的段子:
一个机器学习算法走进酒吧。 酒保问:「您要什么?」 算法回答:「大家都在喝什么?」
这个段子完美地说明了 LLM 的本质:它们不会创造,只会根据统计规律做模式匹配和重组。用到编程语言选择上,这意味着:
- 已经占主导地位的语言将继续占主导地位
- 新语言面临更高的进入门槛
- 创新将主要发生在语法糖和工具链层面,而非根本性变革
未来已来,只是分布不均
William Gibson 的名言「未来已经到来,只是分布不均」在编程语言领域有着特殊的含义。我们以为的「创新」,很多时候只是在重新发现过去:
被重新发现的轮子
| 「现代」特性 | 实际诞生年份 | 首次出现 |
|---|---|---|
| 协程 | 1958 | Melvin Conway |
| 块结构 | 1960 | ALGOL 60 |
| 多重赋值 | 1963 | CPL |
| 迭代器/yield | 1974 | CLU (Barbara Liskov) |
| 管道操作 | 1964 | Doug McIlroy |
| 模式匹配 | 1973 | ML |
| 可选分号 | 1970s | CLU |
| Lambdas | 1936 | Alonzo Church |
我们并非在发明未来,而是在缓慢地重新实现过去。每一代程序员都在「发现」这些概念,就像 Columbus「发现」美洲一样。
为什么进化如此缓慢?
// 编程语言进化的阻力因素
enum EvolutionResistance {
LegacyCodebase {
lines_of_code: u64, // 数十亿行的遗留代码
migration_cost: Cost, // 天文数字的迁移成本
},
HumanFactors {
learning_curve: Duration, // 学习新语言的时间成本
team_coordination: Complexity, // 团队协调的复杂度
mental_models: Inertia, // 思维模式的惯性
},
EcosystemLock {
libraries: Vec<Dependency>, // 庞大的生态依赖
tools: Vec<Tool>, // 工具链锁定
platforms: Vec<Platform>, // 平台兼容性
},
SocialDynamics {
network_effects: Strength, // 网络效应
job_market: Demand, // 就业市场需求
community_size: usize, // 社区规模
},
}
编程语言的进化面临着多重阻力:
- 遗留系统的引力:全球有数十亿行 COBOL 代码仍在运行关键金融系统
- 生态系统锁定:一门语言的价值不在语言本身,而在其生态系统
- 认知惯性:改变思维模式比学习新语法困难得多
- 网络效应:使用者越多,语言越有价值,越难被替代
深度思考:编程的本质是什么?
在这个 AI 辅助编程盛行的时代,Kevlin 的演讲促使我们思考一个根本问题:编程的本质是什么?
语言作为思维工具
编程语言不仅是我们与计算机对话的工具,更是我们思考问题的框架。Sapir-Whorf 假说认为,语言塑造思维。在编程领域,这个假说有着切实的体现:
- C 程序员 倾向于思考内存布局和指针
- Lisp 程序员 自然地想到递归和列表处理
- Haskell 程序员 用类型和纯函数构建心智模型
- Rust 程序员 关注零成本抽象和并发安全
- Go 程序员 用 channels 和 goroutines 思考并发
简约(Simplicity)的价值
Tony Hoare 于图灵奖演讲「The Emperor’s Old Clothes」曾说:「软件设计有两种方式:一种是设计得如此简单以至于明显没有缺陷,另一种是设计得如此复杂以至于没有明显的缺陷。」
现代编程语言的发展趋势似乎倾向于后者。C++ 标准已经厚到「拥有自己的引力场」,JavaScript 每年都在添加新特性,即使是曾经简洁的 Python 也在变得越来越复杂。
但真正持久的创新往往来自简化而非复杂化:
// 简单性的力量:Unix 管道哲学
// 每个程序只做一件事,但做到极致
fn word_frequency(text: &str) -> HashMap<String, usize> {
text.split_whitespace()
.map(|word| word.to_lowercase())
.fold(HashMap::new(), |mut acc, word| {
*acc.entry(word).or_insert(0) += 1;
acc
})
}
未来的编程范式?
如果说过去 60 年的编程语言进化给我们什么启示,那就是:真正的范式转换极其罕见。
我们仍在使用:
- 1960 年代的控制结构
- 1970 年代的抽象机制
- 1980 年代的对象模型
- 1990 年代的泛型编程
即使是看似革命性的创新,如响应式编程、函数响应式编程、Actor 模型等,其根源都可以追溯到几十年前。
实践启示:如何在慢进化时代做出明智选择
1. 选择无聊的技术
「Choose Boring Technology」不是保守,而是智慧。无聊意味着:
- 稳定的语言规范
- 成熟的生态系统
- 丰富的学习资源
- 可预测的未来
2. 深度优于广度
与其追逐每个新语言和新框架,不如深入理解少数几个:
// 深入理解内存模型比学习十种语言的语法更有价值
use std::rc::Rc;
use std::cell::RefCell;
// 理解为什么需要 Rc<RefCell<T>>
// 比知道十种语言的智能指针语法更重要
type SharedMutable<T> = Rc<RefCell<T>>;
3. 原理优于特性
理解编程语言背后的原理比掌握具体特性更重要:
- 理解闭包的本质,而不只是语法
- 理解类型系统的理论,而不只是用法
- 理解并发模型的权衡,而不只是 API
4. 拥抱「够用」哲学
完美是优秀的敌人。在编程语言选择上:
- 够用的语言 + 优秀的设计 > 完美的语言 + 糟糕的设计
- 团队熟悉的语言 > 理论上更好的语言
- 有活跃社区的语言 > 技术上更先进但小众的语言
结语:在重力场中翱翔
回到 Kevlin 的隐喻:编程语言就像重力,无处不在,塑造一切,但往往被忽视。我们无法摆脱重力,但可以学会在其中优雅地翱翔。
编程语言的进化可能永远不会像我们期望的那样快,AI 的介入可能让这个过程更加保守。但这未必是坏事。稳定性带来了可预测性,保守性确保了兼容性,缓慢的进化让我们有时间真正理解和掌握工具。
正如 Bjarne Stroustrup 所说:「只有两种编程语言:一种是人们抱怨的,另一种是没人用的。」我们使用的语言都不完美,但它们足够好,足够稳定,足够强大,让我们能够构建改变世界的软件。
在这个 AI 辅助编程的新时代,理解编程语言的历史、现状和未来趋势,比以往任何时候都更重要。不是因为我们需要预测下一个「大语言」,而是因为我们需要理解:真正的创新不在于语言本身,而在于我们如何使用它们来表达思想、解决问题、创造价值。
未来的编程语言可能不会有革命性的变化,但未来的程序员 —— 懂得历史、理解原理、拥抱简单、追求本质的程序员 —— 将创造真正的革命。
「过去从未死去,它甚至从未过去。」 —— William Faulkner
在编程语言的世界里,这句话有着特别的含义:我们以为的创新,往往是对过去的重新发现;我们期待的未来,可能早已在某个尘封的论文中等待;而我们每天使用的工具,承载着半个多世纪的智慧结晶。理解这一点,也许是成为更好程序员的第一步。