使用 Rhombus 进行灵活的元编程
Flexible metaprogramming with Rhombus

原始链接: https://lwn.net/SubscriberLink/1079001/67840550991151ed/

Rhombus 是一门作为 Racket 项目一部分开发的新编程语言,旨在弥合 Lisp 强大的元编程能力与更易于上手的类 Python 语法之间的鸿沟。通过利用成熟的 Racket 运行时,Rhombus 能够使用已有的生态系统、优化编译器以及高效的数据结构,同时避免了常令潜在用户望而却步的“括号堆砌”风格。 Rhombus 的核心在于其对“面向语言编程”的深度投入。它具备先进的宏系统,允许开发者扩展语言的语法、类型系统和循环结构。这些宏默认是卫生(hygienic)的,并带有丰富的元数据,从而确保了实用的错误提示以及与开发工具的无缝集成。 虽然 Rhombus 提供的性能水平更接近 JavaScript 或 Ruby,而非 Rust 这类系统级语言,但其真正的价值在于其可扩展性。它允许开发者创建特定领域的微型语言来解决复杂问题,而无需从零构建传统编译器。随着 1.0 版本的发布,Rhombus 旨在成为专业人士的实用工具,满足他们在需要深度语言定制的同时,也能享有现代、易读的语法以及详尽的学术级文档的需求。

Hacker News 最新 | 过往 | 评论 | 提问 | 展示 | 招聘 | 提交 登录 使用 Rhombus 进行灵活的元编程 (lwn.net) 16 点,由 spdegabrielle 发布于 1 小时前 | 隐藏 | 过往 | 收藏 | 1 条评论 帮助 dang 34 分钟前 [–] 相关内容。还有其他吗? Rhombus 语言 1.0 - https://news.ycombinator.com/item?id=48633473 - 2026年6月 (113 条评论) 2026年夏季 Rhombus 图片竞赛 - https://news.ycombinator.com/item?id=48546270 - 2026年6月 (3 条评论) Rhombus 语言 - https://news.ycombinator.com/item?id=43394881 - 2025年3月 (158 条评论) Rhombus:宏的新尝试 (2023) - https://news.ycombinator.com/item?id=42041070 - 2024年11月 (1 条评论) Rhombus:基于 Racket 构建的具有传统语法且支持宏扩展的语言 - https://news.ycombinator.com/item?id=41151439 - 2024年8月 (97 条评论) Rhombus(编程语言)现状 - https://news.ycombinator.com/item?id=30314109 - 2022年2月 (17 条评论) 回复 指南 | 常见问题 | 列表 | API | 安全 | 法律 | 加入 YC | 联系 搜索:
相关文章

原文

By Daroc Alden
June 30, 2026

Lisp-like languages have historically led the world in metaprogramming and flexibility. While many modern languages have adopted the idea of macros, Lisp-like languages such as Racket have continued pushing the envelope, attempting to make macros as easy as possible to incorporate into everyday programs. On the other hand, Lisp's minimal, parenthesis-based syntax can be hard to adapt to — to the point that Lisp is sometimes said to stand for "Lots of Irritating Silly Parentheses". Rhombus is a new programming language that aims to have the best of both worlds, marrying Racket's metaprogramming capabilities to a simple Python-like syntax and reasonable standard-library defaults.

The project

The language is part of the broader Racket project, which is "rooted in academia". Of Rhombus's 43 contributors, the two most active are Matthew Flatt and Wing Hei Chan, of the University of Utah and the Chinese University of Hong Kong, respectively. Rhombus is used as a teaching tool at both of their universities the University of Utah; Chan contributes to Rhombus independently, and doesn't actually work in the Chinese University of Hong Kong's computer-science department. Despite that, the language aims to be more than just an academic exercise. Development is supported by the Racket Programming Language Foundation, which seeks to make Racket and Rhombus suitable for real-world professional use. Even though the language only celebrated its 1.0 release on June 22, there is already a set of tools for the Economancy card game partially written in it, among other non-academic uses.

The core of Rhombus is available under either the MIT or Apache 2.0 license, although it is built on top of the Racket runtime, which includes LGPL 3.0 code in some dynamic libraries. That reuse of much of Racket's infrastructure means that it already has a large number of usable libraries, and an optimizing compiler. Rhombus programs can be interpreted, compiled to bytecode, or compiled to native machine code.

One place that Rhombus differs from Racket, other than its syntax, is in its choice of default data structures. Lisp-like languages often use singly linked lists as a core data type, which comes with efficiency problems. Rhombus uses lists backed by an immutable tree structure that makes many operations take logarithmic time. Flat, mutable arrays, tree-based maps and sets, and hash-based maps and sets are also available in the standard library.

This LWN.net subscription-only content has been made available to you by an LWN subscriber. To see more of this content, please take advantage of the following special offer.

Free trial subscription

Try LWN for free for 1 month: no payment or credit card required. Activate your trial subscription now and see why thousands of readers subscribe to LWN.net.

The syntax

Rhombus's syntax is fairly similar to Python in that it is indentation-based, and uses ":" to indicate the start of an indented block. One slight difference is that Rhombus uses the "|" symbol to separate branches in if expressions and similar contexts. The clearest way to explain is probably with an example (an implementation of the factorial function):

    fun factorial(n):
      if n <= 1
      | 1
      | n * factorial(n - 1)

Programmers with a more imperative bent might prefer the version below that uses a for loop. Note the use of an inclusive range, 1..=n rather than the more common exclusive range 1..n.

    fun factorial(n):
      def mutable p = 1
      for (i in 1..=n):
        p := p*i
      p

By default, variables are immutable, so the mutable binding operator is needed to let the loop alter the value of p. The example can be made shorter, however. Rhombus's for loops support a feature that is uncommon outside of Lisp-like languages: an optional "reducer" that is used to combine the result of each loop into a final value for the loop itself. The math.product reducer multiplies all of the values returned from the loop, allowing one to write:

    fun factorial(n):
      for math.product (i in 1..=n):
        i

This ability means that Rhombus's equivalent of the list comprehensions found in other languages is just a for loop that uses the List reducer to collect results into a list. For loops can also produce maps, sets, or even boolean values using the all or any reducers. In a typical programming language, one might expect this kind of built-in syntax to work for the types provided by the standard library, but not for user-defined types. Rhombus's macros, however, can be used to extend more or less any part of the language, including creating new reducers. Most programs will probably not need to do so, but it is nice to have the option when it would make the program simpler.

Macros

Macros in Rhombus use single quotes to produce syntax objects that represent a fragment of Rhombus code, with "$" used to indicate placeholders. Unlike traditional Lisp-like languages that represent code with lists, Rhombus's syntax objects are their own data type. This lets them carry additional metadata such as line number and position, which in turn lets Rhombus produce helpful error messages, such as stack traces that take macros into account correctly. Syntax objects also carry metadata that controls how names are resolved, giving programmers control over when and how macros capture names from the environment. Both hygienic and non-hygienic macros are possible to write, although hygienic macros are the default.

As an example of a small macro, here is all of the code needed to implement Ruby's postfix if in terms of the built-in when macro:

    import:
      rhombus/meta open // Needed for defining a macro

    expr.macro '$action if $test':
      'when $test
       | $action'

    println("Hi!") if (foo == bar)

In some ways, this kind of syntax object is reminiscent of the approach that TemplateHaskell uses. Unlike TemplateHaskell, however, Rhombus macros can be defined anywhere that a normal function could be, and respond to the grammatical context in which they are used. The example above is a macro that can be used anywhere that an expression is expected. Reducer macros are defined with reducer.macro, and there are similar constructs for macros that customize the handling of imports, pattern matches, and so on.

One might expect that the list of grammatical contexts in which macros can operate to be fixed. That isn't true in Rhombus, which exposes functions for manipulating grammatical contexts to the user. As long as the program uses Rhombus's basic indentation-based syntax, the programmer is free to define their own grammar and kinds of expressions, should they think it necessary.

Other forms of extension

Another example of that kind of flexibility is Rhombus's approach to type checking. Rhombus is, in some sense, not a statically typed language. It can look like it is, though, if one adds type annotations:

    fun factorial(n :: Int) :: Int:
      def mutable p :: Int = 1
      for (i in 1..=n):
        p := p*i
      p

    // Calling factorial("foo") results in an error message:
    // factorial: argument does not satisfy annotation
    //  argument: "foo"
    //  annotation: Int

Rhombus allows programmers to attach arbitrary static information to expressions, and define rules for how that static information should be inferred, extended, checked, and used to generate machine code. By default, the language treats annotations as part of a gradual type system that infers types as much as possible, and then uses that to provide error messages and generate better machine code. But there is nothing stopping the programmer from attaching their own classes of static information to Rhombus programs. For example, it is possible to define a system of annotations for tracking the flow of sensitive data through a program, or for tracking the asymptotic run-time of a function. Macros can access this static information in order to compile expressions differently depending on the inferred value.

Rhombus's tendency to expose all of the implementation details of the language within the language itself is part of the project's commitment to language-oriented programming. For many tasks, users will have no need to reach for macros; the ordinary tools provided by the language are sufficient for every-day use. For those tasks where Rhombus is not already a good fit, for whatever reason, the language encourages the creation of miniature embedded domain-specific languages, to make expressing the core logic of the problem as simple as possible.

In many languages, a certain amount of boilerplate is necessary, especially for programs that are best described in terms of data structures or relationships that aren't supported as part of the standard library. Rhombus allows programmers to write that boilerplate once, in a macro, and then focus on expressing their program in whatever way makes the most sense to them, even when that requires looking deep into the semantics of loops or the action of the type system. Many such miniature language extensions can coexist within the same program, and the additional metadata attached to syntax objects allows for the creation of generic code formatters and highlighters, jump-to-definition support that handles custom binding macros, and the other conveniences expected of a modern language.

In support of its extensibility, Rhombus has extensive documentation covering all parts of the language, from parsing all of the way through to compilation. Rhombus inherits the Racket community's penchant for thorough documentation, which is a blessing when one needs to investigate the details of some part of the language, but which can be a bit verbose when one just needs a simple overview.

Rhombus's niche

Overall, Rhombus's best features are definitely its extensibility and documentation. In other areas such as compilation speed, speed of execution, package ecosystem, error messages, and so on it is solidly middle-of-the-road: good enough not to get in the way, but not so good that it outshines existing languages that focus on those areas. Rhombus code, even with full type annotations, is unlikely to challenge C or Rust in speed, for example — it typically has performance between JavaScript and Ruby, although the details vary between programs and benchmarking setups.

Where Rhombus is likely to be most useful is in those problem domains that have custom home-grown languages or that are awkward to express with existing approaches. Programmers have always had the ability to write new special-purpose programming languages, but the amount of work that goes into writing a traditional optimizing compiler is enormous. Writing a set of Rhombus macros is much easier, and gives one an existing compiler, existing libraries, usable syntax highlighting, error messages, and editor integration for free, while preserving the ability to customize the operation of the language at the deepest levels, should it prove necessary.




联系我们 contact @ memedata.com