了解Ada这个不同时代的编程语言
1 简介
Ada 是 1980 年代初期由美国国防部推动开发的一种编程语言,其主要目标是提高大规模、长期维护项目的安全性、可靠性和可维护性。
虽然 Go(Golang)是在 2009 年发布的,目标不同(强调简洁、并发和工程效率),但许多 Go 的优秀特性,其实可以从 Ada 中看到早期的“影子”或思想来源。
这里从多个维度分析 Ada 的一些先进设计理念,以及这些理念如何在 Golang 中被借鉴、继承或重新诠释。
2 Ada语言
它有大量极其复杂的函数式语言、形式定理证明器,以及…Ada,一种可追溯到 1983 年的随机政府语言。
此外,您还会注意到,在任何 Ada 文档中,在解释依赖类型的工作原理时,他们都没有觉得有必要提出集合论或 λ 立方体:P
这种高度形式化的学术 / 研究 / 娱乐语言与一种非常官僚主义的语言并置,后者的设计理念与 COBOL 的设计理念非常相似。
但是,如果函数式程序员 / 类型论的人来问关于 Ada 的依赖类型的问题,这是写一篇大文章来解释 Ada 中的依赖类型的动力。Ada说明设计人员是如何真正尝试实现特定目标的,而巧合的是,他们的解决方案符合依赖类型的正式定义。
请注意,我真的根本不了解函数式编程或类型论,所以我不能真正谈论太多,---- 我对 Ada 不熟练。
3 Ada 的依赖类型
首先,为了上下文,这里对我所说的内容进行了非常简化的总结,因为它是一个罕见的功能:依赖类型只是依赖于某些具体值的类型。
因此,例如,具有数组字段的结构体,其长度取决于结构体中的另一个字段,这是 Ada 依赖类型2 的主要应用之一。
我还需要谈谈 Ada 的一个设计理念,即避免动态分配,除非出于安全原因确实需要
因为它没有像 Rust3 那样的完全安全分配的借用检查器。关键问题是,当您使用一个调用堆栈同时存储返回地址和局部变量时,被调用函数无法向上返回其堆栈分配的变量之一的内容,因为被调用方需要能够弹出返回地址,并使堆栈指针精确地位于调用之前的位置。
相反,任何大于 register 的返回值都必须由调用者分配其堆栈槽,其大小为调用者所知。Ada 通过第二个非调用堆栈4 解决了这个问题,函数可以在该堆栈上分配编译时未知空间并向上返回该空间,而无需动态分配并保留堆栈提供的良好范围和自由词法释放。
在 Ada 中真正需要动态分配的唯一时间是当您与 C/C++/Fortran/COBOL5 互通时,或者如果您的聚合类型包含未知长度的对象,例如字符串数组,您必须动态分配所有字符串,
然后可以像往常一样处理指针数组(Ada 具有丰富的容器库,因此您很少需要考虑它all) 的 S Ban允许使用第二个堆栈而不是动态分配(即避免在其他地方需要指针,以便将未定义长度的对象封装在另一个对象中)是 Ada 依赖类型的一种样式的关键动机。
4. 强类型与类型建模(Type Modeling & Safety)
Ada 的特性(1983)
Ada 强调 类型的语义建模:不仅是“整数”,而是“米数”、“秒”、“角度”等带有含义的数值。
支持 强类型派生(Strong Typing / Type Derivation):
type Meter is new Float;
type Second is new Float;
– 即使它们都是 Float,也不能混用
编译器检查语义一致性,避免因类型误用导致的 bug。
- Go 的类比
Go 虽然是静态类型语言,但强调的是“简洁”和“实用”,类型系统没有 Ada 那么强,但也具备一些类型安全的特性:
明确类型定义 type Celsius float64 等可避免误用。
接口类型(interface)则体现另一种“意图表达”。
Go 的 go vet 等工具有助于静态分析,这也是 Ada 的早期核心哲学。
启发:
Ada 的类型系统关注“意义建模”,Go 则以轻量级方式实现相似目标:通过显式类型、工具链确保语义正确。
4. 并发模型(Concurrency)
Ada 的特性
提供“任务(Tasks)”为语言级并发模型,1983 年就具备:
task type Worker is
entry Start;
end Worker;
支持“Rendezvous”同步方式,表达安全同步点。
Go 的特性
Go 的并发模型建立在 goroutine + channel 机制上,强调 CSP(Communicating Sequential Processes)。
go func() { ch <- result }()
✅ 启发:Ada 是最早将并发视为一等公民的语言之一,Go 沿用这一理念,但用更现代、简洁的语法实现了广泛普及。
5 安全与健壮性(Safety & Robustness)
Ada 的特性
提供运行时检查:数组越界、空指针、除零等。
“设计即为正确”的理念:通过类型、范围检查等将 bug 杜绝在编译时或早期运行时。
Go 的特性
没有泛化的运行时检查(如越界 panic 可能崩溃),但仍提供一些内建检查机制。
Go 的 philosophy 是“make it simple enough to be safe”,而不是像 Ada 那样构建复杂机制。
启发:两者都在语言级支持更高的代码安全,但方式不同,Ada 偏向保守/形式化,Go 偏向实用主义/自动化。
6 模块化与封装(Modularity)
Ada 的特性
强大的包(Package)机制:
明确分离接口(spec)与实现(body)
清晰的封装和信息隐藏。
Go 的特性
简单的包机制:每个文件夹为一个包。
无类,但支持通过组合、接口实现封装。
启发:Ada 提供了一种精确控制暴露和隐藏信息的方式,Go 虽然简单,但也借鉴了清晰分层和模块边界的思想。
7 编译器为中心(Compiler-Oriented Philosophy)
Ada
语言设计鼓励编译器尽可能在编译期捕捉错误,强调 编译器理解程序意图。
可用于形式验证(SPARK/Ada)。
Go
Go 的编译器极快,鼓励频繁构建、快速反馈。
静态工具链(如 go vet, go fmt, go mod, go generate)继承了“编译器驱动开发”的哲学。
启发:Ada 的理念是“聪明的编译器为聪明程序员服务”,Go 则是在工程效率层面践行这一思想。
8 小结:Ada 与 Go 的理念交集
特性 Ada(1983) Go(2009) 类比或演化
类型建模 强类型 + 类型语义 简洁类型定义 + 明确接口 Go 的 type 定义受启发
并发模型 Task + Rendezvous Goroutine + Channel 都是语言内建并发
模块系统 Package 分 spec/body 包与接口组合 不同方式实现信息封装
静态检查 编译时/运行时严格检查 快速编译 + 工具支持 Go 用工具链实现一部分 Ada 的特性
编译器哲学 编译器理解程序员意图 编译器和工具链驱动开发 相似理念,不同实现风格
虽然 Go 和 Ada 分别代表了两个时代(高可靠 vs 高效率)的语言哲学,但它们都体现了一种 “让程序更安全、更清晰、更工程化” 的共通追求。
Go 没有直接继承 Ada 的语法或结构,但在设计理念上,“通过类型、并发模型和编译器工具提升程序正确性” 是两者的重要交集。
如果你感兴趣,我也可以进一步提供具体代码对比,展示 Ada 与 Go 在类型、并发、模块等方面的异同。
- 点赞
- 收藏
- 关注作者
评论(0)