编程语言中的一些好点子

Hacker News Top 新闻

摘要

本文讨论了三种编程语言特性:用于在Crystal和TypeScript等静态语言中实现动态感觉的流类型、用于确保Rust内存安全的借用检查,以及用于D语言中不变性检查的契约编程。

暂无内容
查看原文
查看缓存全文

缓存时间: 2026/09/12 17:32

# prydt的个人网站 来源:https://prydt.xyz/blog/a-few-good-ideas-in-pl/ ## 编程语言中的几个优秀设计 --- **发布日期:**2026\-05\-10** 作者:Pranoy Dutta --- 以下是我钟爱的几个编程语言特性: ## 1\. 流式类型 这个绝妙的概念我首次在Crystal(https://crystal-lang.org/)中接触到,它指的是*流式类型*\。 Crystal是一门编译型编程语言,具备静态类型检查,语法与Ruby编程语言(https://www.ruby-lang.org/en/)高度相似。Crystal之所以能保持动态类型语言的质感,关键在于变量在其生命周期中可以被赋予不同类型——这与我使用过的大多数静态类型语言截然不同。示例如下: ``` my_var = 5 # 此时 my_var 的类型为 Int32 assert(my_var.is_a?(Int32)) if some_complex_condition() my_var = "hello!" # 此时 my_var 的类型为 String assert(my_var.is_a?(String)) end # 此时 my_var 的类型是什么? # # 是 Int32 | String assert(my_var.is_a?(Int32 | String)) ``` 最有趣之处在于:`my_var`在某些时刻确实是`Int32`,在特定代码区间内可确认为`String`,但在另一些区间中编译器无法确定其具体类型……因此其类型会被定义为所有可能性的并集。此时若试图对`my_var`调用`String`方法将导致编译失败,因为它本质上是`Int32 \| String`。编译器会强制要求添加`if my_var.is_a?(String)`这类检查,从而将类型范围*收窄*为仅`String`。 这是利用精妙的类型推断,让编译型语言在几乎不增加运行时开销的前提下*呈现*动态特性绝佳范例。 TypeScript同样具备流式类型(https://www.typescriptlang.org/docs/handbook/2/narrowing.html#control-flow-analysis)与类型收窄(https://www.typescriptlang.org/docs/handbook/2/narrowing.html)功能! ## 2\. 借用检查 Rust(https://rust-lang.org/)是一门系统编程语言,能在无需垃圾回收器的情况下保证内存安全。 并发程序中一类主要的内存安全缺陷是恼人的数据竞争:当多个线程在未同步的情况下同时读写同一内存地址时就会发生。 借用检查机制正是Rust能在编译期静态预防数据竞争的关键。它强制执行以下规则(https://doc.rust-lang.org/1.8.0/book/references-and-borrowing.html): - 任何借用不得超越所有者的作用域 - 以下两种情况二选一: - 恰好1个可变引用(`&mut T`) - 或1个以上不可变引用(`&T`) 这或许会让你联想到读写锁(https://en.wikipedia.org/wiki/Readers%E2%80%93writer_lock)——一种允许多个读者或单个写者同时访问的锁机制。其原理在于:要预防数据竞争,只需在读写操作之间进行同步即可。并发同步的核心目的,就是将特定内存地址的写入操作序列化。 我钟爱借用检查机制,因为它是解决数据竞争问题如此优雅的方案,属于零成本抽象——仅增加编译期检查和些许复杂度。复杂度的增加是客观存在的权衡,但若编写并发程序,这种复杂性本就是问题域与生俱来的特质。 ## 3\. 契约式编程 若你拥有一定编程经验,或许已接触过基础的`assert`语句——当特定不变式被破坏时,它将触发错误或报告。每个程序都需要维护某些不变式,例如“此日期永远晚于另一日期”或“此树始终保持平衡”。 D编程语言(https://dlang.org/)(一门被低估的语言)支持契约式编程(https://dlang.org/spec/contracts.html),为描述更复杂的不变式提供语法支持,这些不变式可存在于函数级或对象级。 D具备标准`assert`语句: ``` double always_positive = 100; assert(always_positive > 0); ``` 但D还提供`enforce`以区分语义:`assert`用于检测程序不变式违规,触发时表明程序存在正确性缺陷;而`enforce`则因外部问题(如用户输入越界或环境异常)抛出异常。 ``` enforce(length >= 7, "Must be at least 7."); ``` 此外,D通过语法支持函数的前置与后置条件。以下示例取自《Programming in D》(https://ddili.org/ders/d.en/contracts.html): ``` int daysInFebruary(int year) out (result) { assert((result == 28) || (result == 29)); } do { return isLeapYear(year) ? 29 : 28; } ``` 其中`daysInFebruary`函数定义了后置条件:其返回值必须为28或29,其他结果均表明函数存在逻辑错误。 最后,D还支持类级不变式,用于确保对象数据始终处于一致状态。以下是一个综合示例: ``` class BankAccount { private double balance; invariant() { balance >= 0; // 对象级不变式,始终被检查 } this(double initialBalance) in (initialBalance >= 0, "Initial balance cannot be negative") { balance = initialBalance; } void deposit(double amount) in (amount > 0, "Deposit amount must be positive") out (; balance == balance + amount) // 方法返回后检查 { balance += amount; } void withdraw(double amount) in (amount > 0, "Withdrawal amount must be positive") in (amount <= balance, "Insufficient funds") out (; balance >= 0, "Balance must remain non-negative") { balance -= amount; } double getBalance() out (result; result >= 0, "Balance returned must be non-negative") { return balance; } } ``` 相比在每个类方法起止处编写一致性检查函数,`invariant()`块更加简洁且易于维护,且具备明确的语义表达。 —— Pranoy

相似文章

快速与硬核代码

Armin Ronacher

本文探讨了大语言模型如何降低编程语言选择的摩擦,促使开发者采用如 Rust 和 Zig 等面向性能的语言来开发快速、小型的软件,并使得处理以前被认为困难的技术成为可能。

静态类型与铲子(2026)

Lobsters Hottest

作者认为,2010年代静态类型编程的复兴归功于改进的类型系统(例如 TypeScript、Haskell、Rust),这些系统提供了可空类型处理、和类型以及类型推断,与 Java 和 C++98 等早期语言中糟糕的静态类型形成对比。

并发、交互、可变:三者选二

Hacker News Top

文章探讨了编程语言中并发、交互性和可变性之间的固有权衡,通过Common Lisp、Python、Ruby和Erlang的例子说明没有语言能完全优化这三者。

无类型检查的生命周期借用检查

Lobsters Hottest

一篇博客文章介绍了一种玩具语言,它在运行时而非静态类型系统中强制执行借用检查,通过在栈上使用轻量级引用计数来支持内部指针和单一所有权,适用于动态类型环境。

内存安全绝对主义者

Lobsters Hottest

本文批评了编程语言辩论中的内存安全绝对主义,认为像 Fil-C 这样的新方法也有权衡,而将 Rust 视为不安全忽略了实际好处。