金钉子:复活 Vale(n) 编程语言

Lobsters Hottest 工具

摘要

这篇文章讨论了一个雄心勃勃的项目,旨在复兴Vale编程语言并创建一种名为Valen的新语言,目标是实现与Rust的无缝集成,以支持跨语言泛型和内存安全。

<p><a href="https://lobste.rs/s/fjnkn4/golden_spike_resurrecting_vale_n">评论</a></p>
查看原文
查看缓存全文

缓存时间: 2026/09/17 15:38

# 金道钉与Vale(n)编程语言的复活 来源:https://verdagon.dev/blog/golden-spike-reviving-vale-valen 让我们做些极其雄心勃勃且极不理智的事 2026年9月17日——[](https://en.wikipedia.org/wiki/Golden_spike) 1869年5月10日曾有一个不可思议的时刻,铁路建设者们终于实现了**将东海岸铁路网与西海岸铁路网连接**的目标。那一刻,美国首条横贯大陆铁路诞生了。0 (https://verdagon.dev/blog/golden-spike-reviving-vale-valen#note0) 经过六年的努力,1 (https://verdagon.dev/blog/golden-spike-reviving-vale-valen#note1)两条铁路网终于在犹他州的普罗蒙特里峰交汇。为纪念这一刻,他们在铁路最后一根枕木上钉入了重17.6克拉的**金道钉**。 对我而言,“金道钉(https://en.wikipedia.org/Golden_spike)”这个短语意味着完成一项极其艰巨的任务,将两个独立而遥远的系统连接起来。2 (https://verdagon.dev/blog/golden-spike-reviving-vale-valen#note2) 在编译器领域,常常感觉每个编译器都与其他编译器*完全隔绝*。当一门语言需要调用另一门语言时,通常需要通过C ABI进行复杂的转换,编写“C绑定”(包装函数),有时还得与各路神灵做交易。即便如此,跨语言泛型3 (https://verdagon.dev/blog/golden-spike-reviving-vale-valen#note3)仍然无法实现(C没有泛型),且跨越边界绝对无法保证内存安全(因为是C!)。 **这就是为何不存在Rust++语言的原因;**在C边界上实现内存安全和泛型是不可能的。即使可能,集成两个编译器也*极其困难*。于是我暗自思忖,这*听起来像是2026年值得追求的目标!* 于是我们来到这里!本文将记录我这个*极其不自量力*的尝试的开端——创建一门**真正的类Rust++语言**,其编译器能与rustc无缝对话,使我们能够使用我的Rust图形库4 (https://verdagon.dev/blog/golden-spike-reviving-vale-valen#note4),并实现跨语言泛型、内存安全、线性类型、Nick Smith的组借用检查(https://verdagon.dev/blog/group-borrowing),以及其他众多诱人的特性。 暂且将这个新语言命名为“Valen”5 (https://verdagon.dev/blog/golden-spike-reviving-vale-valen#note5)6 (https://verdagon.dev/blog/golden-spike-reviving-vale-valen#note6),因为它与我现有的语言Vale (https://vale.dev/)相似(但已足够不同)。请继续阅读,本文将讲述迄今的探索历程! 整个尝试*极具实验性*,许多部分存在漏洞和粗糙边缘(参见旁注7 (https://verdagon.dev/blog/golden-spike-reviving-vale-valen#note7))。我将花费数月时间清洗、重写和巩固这个“恐怖之物”,然后才能将其推向世界。欢迎查阅源代码(https://github.com/valen-lang/valen),但请当心,此处有龙出没! --- ### 金道钉与Vale(n)编程语言的复活 0 本文0%由AI撰写。更多思考见此处(https://verdagon.dev/blog/personal-ai-policy)。我的立场:若想让人花费时间阅读,就请亲自花费时间写作。感谢阅读=) 1 姑且称之为*一团糟*。我最喜欢的部分(来源(https://cprr.org/Museum/Bowman_Last_Spike_CHS.html)): “……在这日期前两天,联合太平洋铁路在皮德蒙特遭遇新麻烦:副总裁T·C·杜兰特博士乘坐的车厢被约400名工人(一名记者称500人)解挂并推至侧线,这些工人要求支付自1月1日起拖欠的工资。绝对的狠人!” 2 在Google Earth上,我曾将本地编辑应用连接至云存储,终于实现了在线编辑。我们将那个项目也称为“金道钉”。这次的Rust互操作探索让我回想起那个时刻! 3 下文将详细解释,但简而言之,能够使用`SomeRustStruct`。 4 这是一个基于awgpu(https://wgpu.rs/)的屏幕空间折射图形库,我称之为“Glass Domino”。天啊,我*真想*多聊聊它的工作原理,以及它如何高效实现重叠折射器。但要集中注意力,Evan!本文是关于编译器和语言的! 5 由于其编译器是`valenc`,我们可以将其读作“Valence”,听起来不错! 6 另外,对我而言很有趣的是,如今V、Val、Vale、Vala和Valen都已成为真实存在的语言。我有点想发布一个小型Rust互操作语言供他人使用,并命名为Va。 7 需要明确当前可工作的功能: - Valen拥有线性类型,但尚不能声明现有Rust类型为线性类型。 - Valen支持组借用(闭包除外),且能跨边界进行借用检查。 - 结构体可跨边界工作,即使是实现了Rust trait的Valen结构体。但它们必须是零大小的(填充结构体在我的独立原型中可用,尚未移植到Valen)。 - 代际引用暂时禁用,有望不久后恢复。 8 特指绿龙。 --- ### 一个愿望、一个希望与一门梦想语言 Rust是我最喜欢的语言之一9 (https://verdagon.dev/blog/golden-spike-reviving-vale-valen#note9),因其速度、安全性和生态……但有些特性若能改进,对我的用例来说将**更简洁且更完善**。我的愿望清单: - 一个摆脱共享互斥性(https://verdagon.dev/blog/group-borrowing)的借用检查器 - 更快的运行时性能10 (https://verdagon.dev/blog/golden-spike-reviving-vale-valen#note10) - 通过线性类型(https://www.youtube.com/watch?v=IpuvQUVB8Cg&t=2s)实现资源安全 - Zig风格的编译时计算(https://verdagon.dev/blog/impossible-optimization)11 (https://verdagon.dev/blog/golden-spike-reviving-vale-valen#note11) - 代际引用(https://verdagon.dev/blog/generational-references) - 无需`RefCell`或`Cell`即可持有可变数据的`Rc` - ……以及众多其他特性12 (https://verdagon.dev/blog/golden-spike-reviving-vale-valen#note12) 但我不只是想要一门新语言,否则将无法访问Rust的库生态。那么我们如何兼顾这些,同时仍能调用Rust代码? 9 你猜对了,C++、Scala和Rust三者并列! 10 我*认为*这可行,但仍需实现以进行基准测试,因此请持保留态度:要理解我的意思,请查看组借用文章(https://verdagon.dev/blog/group-borrowing)中的“预览”示例。我相当确信它比对应的Rust程序更快,因为Rust程序需要两次额外的`.get_mut(id)`查找(加上一些额外的错误处理分支)。此外,组借用应能向LLVM表达`noalias`/`alias.scope`信息,从而不会丢失这些优化。更多详情参见此文档(https://docs.google.com/document/d/1OxnwYe704m3TtUhNnR5lQzyOxEoZKUV-omMA5sGJHmQ/edit?usp=sharing)。 11 零成本编译器优化的领域特定语言,有人感兴趣吗? 12 ……因为我篇幅用尽了。但现在我们在旁注中!其他特性包括:更好的异步故事、更佳的枚举、更优的`mustprogress`(https://llvm.org/docs/LangRef.html)优化、闭包实现trait、通用函数调用语法、良好的编译时间,以及*也许*若我们足够幸运,能恢复部分完美可重放性(https://verdagon.dev/blog/perfect-replayability-prototyped)。 --- ### 目标 这曾因诸多原因看似不可能,详见《穿越不可能的FFI边界,与我的逐渐疯狂》(https://verdagon.dev/blog/exploring-seamless-rust-interop-part-2)。我忘了那篇文章包含此图: ![](https://verdagon.dev/blog/golden-spike-reviving-vale-valen/images/ffimeme.png) 希望我们不必如此!(*……伏笔渐浓……*13 (https://verdagon.dev/blog/golden-spike-reviving-vale-valen#note13)) 总之,**更具体、明确的目标**是让此程序运行:14 (https://verdagon.dev/blog/golden-spike-reviving-vale-valen#note14) ``` import rust.nobiliav.NobiliaWindow; import rust.nobiliav.FrameInput; import rust.nobiliav.MainLoopCallback; ... // Import constants exported func main() int { w = NobiliaWindow.new(1200, 900); ... // set up the terrain and entities w.main_loop( // Make a Valen closure that implements the Rust `trait MainLoopCallback` &MainLoopCallback((win, input) => { key = input.key(); if (key == key_arrow_left()) { win.rotate_camera(-4, 0); } else if (key == key_arrow_right()) { win.rotate_camera(4, 0); } else if (key == key_arrow_up()) { win.rotate_camera(0, 4); } else if (key == key_arrow_down()) { win.rotate_camera(0, -4); } })); return 0; } ``` 当然,这需要回答*许多*非常困难的问题。我将在代码中提出: ``` // How do we know what things are importable from Rust? import rust.nobiliav.NobiliaWindow; import rust.nobiliav.FrameInput; import rust.nobiliav.MainLoopCallback; ... exported func main() int { // How do we know what static methods exist in a Rust type? // How does Valen know what parameters rustc expects here? w = NobiliaWindow.new(1200, 900); ... w.main_loop( // How do we make a Valen closure implement a Rust trait? // How do we know the methods on a Rust trait? &MainLoopCallback( // How will Rust call BACK into Valen code? Monomorphizer integration? // Can the optimizer inline a Valen function into a Rust function and vice versa? (win, input) => { key = input.key(); if (key == key_arrow_left()) { win.rotate_camera(-4, 0); } else if (key == key_arrow_right()) { win.rotate_camera(4, 0); } else if (key == key_arrow_up()) { win.rotate_camera(0, 4); } else if (key == key_arrow_down()) { win.rotate_camera(0, -4); } } )); return 0; } ``` 然而,这些问题隐藏着底下真正核心的问题。 13 当时,我认为需要重新实现Rust的泛型和trait系统。这将很不幸,尤其是Valen*已经拥有*泛型和trait系统(从Vale编译器演化而来)。后来我意识到可以做个聪明操作:当Valen代码想要调用Rust函数时,Valen编译器*不应该*查看Rust签名。相反,它应该首先(懒惰地)生成*在Valen AST中对应的函数签名*,而Valen已能理解该签名。这可能听起来不太明白,欢迎访问Valen discord(https://discord.gg/SNB8yGH),我很乐意解释更多。 14 好吧,我*必须*快速极客一下这部分。看到`MainLoopCallback((win, input) => {...})`部分了吗?那是我们*内联创建*Rust trait `MainLoopCallback`的*新子类*。这称为开放接口构造函数(https://vale.dev/guide/interfaces#open-interface-constructors),在Vale编译器中也存在。它是最复杂的特性之一,因为它处于接口、泛型和闭包的交汇点——一场完美风暴。当我为此次尝试将Vale编译器迁移到Rust时(这本身就值得一篇博客文章),我必须优先处理哪些特性恢复,而我*绝对*不想处理开放接口构造函数。结果我还是处理了! --- ### 核心问题 如许多人所知,其他语言调用Rust的最常见方式是让Rust库将函数暴露为`extern "C"`,类型暴露为`repr(C)`。这是因为C是底层语言间的事实通用翻译器。同时也因为Rust尚无稳定ABI。(https://www.reddit.com/r/rust/comments/ss2p6c/what_does_it_mean_when_people_say_that_rust_does/)15 (https://verdagon.dev/blog/golden-spike-reviving-vale-valen#note15) “什么是ABI?”你可能会问。简化来说,“ABI”基本上是Rust回答这些问题的方式: - “若用户调用函数时按值传递结构体,我们编译为按引用传递,还是放入寄存器?” - “若用户指定一个`bool`、一个`u64`整数再一个`bool`,我们是否将两个`bool`相邻放置以节省空间?”16 (https://verdagon.dev/blog/golden-spike-reviving-vale-valen#note16) 唉,Rust尚未承诺采用特定ABI。因此,其他语言调用Rust的唯一选项是使用C ABI(通过`extern "C"`和`repr(C)`)。 **但C没有泛型。** C中不存在`void do_something(T* thing)`这种东西。这意味着其他语言无法调用泛型Rust函数。 **这对我们是个问题,**因为……回想上面那个`main_loop`调用: ``` exported func main() int { ... w.main_loop(&MainLoopCallback((win, input) => { ... })); ... } ``` 以下是Rust中`main_loop`的真实面目: ``` impl NobiliaWindow { pub fn main_loop(&mut self, cb: &mut C) { ... } ... } ``` 没错,那个参数是*泛型*的,接受任何实现`trait MainLoopCallback`的类型。但C ABI没有泛型!而我们需要泛型。该死!我们卡住了。 因此**核心问题,**核心要解决的谜题是:*如何实现跨语言泛型?* 15 “ABI”代表“应用程序二进制接口”。 --- ### 跨语言泛型 这个问题其实有两个层面: 1. 我们如何调用Rust泛型函数? 2. 我们如何调用一个*回调Valen*的Rust泛型函数?17 (https://verdagon.dev/blog/golden-spike-reviving-vale-valen#note17) 以下是一个更简单的示例来说明第一层面: ``` import rust.std.vec.Vec; exported func main() int { my_vec = Vec(); my_vec.push(21); my_vec.push(42); // Returns 42 return my_vec.pop().unwrap(); } ``` 当我们说`my_vec.push(21)`时,我们是在调用泛型函数`Vec::push`。幸运的是,在《疯狂》(https://verdagon.dev/blog/exploring-seamless-rust-interop-part-2)文章中,我设法让它在C中工作了: ``` // Import a rust type directly (no bindings!) #pragma rsuse VecInt = std::vec::Vec // Must specify each method you want to use #pragma rsfn VecInt_with_capacity = VecInt::with_capacity #pragma rsfn VecInt_capacity = VecInt::capacity #pragma rsfn VecInt_drop = VecInt::drop // Plus the magic incantation, and... #include "rust_deps/rust_deps.h" #include <stdio.h> int main() { // ...presto, we can use rust libraries! VecInt argv = VecInt_with_capacity(42); printf("Capacity: %lu\n", VecInt_capacity(&argv)); VecInt_drop(argv); return 0; } ``` 我称其为“最诡异的探索”,因为它必须进行如下特技操作: - 运行`rustdoc`(是的,那个文档生成器!)并用`rustdoc_types`库解析其JSON输出,以确定哪些类型可用,哪些`impl`有哪些方法。 - 用一个“侦察程序”运行`rustc`,该程序只打印大小:`println!("{}", size_of::<T>())`打印24字节。(感谢matklad和literallyvoid帮我改进这部分!) - 然后用一个“实例化程序”再次运行`rustc`,生成一个C程序可静态链接的“包装C库”。 - Vale的内存安全方法与Rust不完全匹配,因此用户必须遵循某些模式。 它有一些限制: - 我们需要完全指定泛型参数,如`VecInt = std::vec::Vec`。 - 我们需要在`#pragma rsfn`中导入*每个*使用的方法。 但最棘手的限制是我们无法让C类型实现Rust trait。这意味着你不能使用`HashMap::get`,因为它的键必须实现`Hash`和`Eq` trait。 尽管如此,尽管诡异,2024年的解决方案实际上是个相当好的起点。上文提到跨语言泛型问题有两个层面: 1. 我们如何调用Rust泛型函数? 2. 我们如何调用一个回调Valen的Rust泛型函数? 2024年的解决方案在精神上解决了大部分第1层面,尽管它获取Rust信息的方式……

相似文章

Révo编程语言

Lobsters Hottest

Revo 是一种编程语言,具有清晰的数据流、错误即值、编译时执行和基于纤程的并发特性。它使用Zig构建,并提供可选类型和强推断功能。

V语言评测 (2023)

Lobsters Hottest

本文是对2023年V语言的全面评测,批评了其文档质量、内存管理缺陷以及功能不完整等问题。

编程语言中的一些好点子

Hacker News Top

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

持久递归世界实现自主软件演化

Hugging Face Daily Papers

介绍了 EvoX Genesis,一个围绕持久项目而非持久代理组织长周期软件开发的框架,以低成本和高性能自主构建了基于 Rust 的 C 编译器,并重新实现了 MESA 数值模块。