Racket's Evolution: Why You Should Care About the Rhombus 1.1 Release

Hey everyone, Alex here. Welcome back to another edition of Coding with Alex at sysseder.com.

If you have been hanging around functional programming circles for any length of time, you’ve probably run into Racket. Descended from Scheme and Lisp, Racket has long been celebrated as the ultimate "language for making programming languages." It is a playground for language design, syntax-directed translation, and macro-systems. But let’s be honest: for a lot of mainstream developers, the endless sea of parentheses (s-expressions) has always been a tough pill to swallow.

Enter Rhombus.

Originally conceived as "Racket 2", Rhombus is a brand-new dialect built on top of the Racket virtual machine (CS/Chez Scheme). Instead of traditional s-expressions, Rhombus introduces a modern, elegant, and highly readable indentation-based syntax, combined with an incredibly powerful, extensible macro system that works over *syntax objects* rather than raw text. With the recent release of Rhombus 1.1, this ambitious project has reached a level of stability and feature-completeness that makes it ready for prime-time exploration.

Today, we are going to dive deep into what Rhombus 1.1 brings to the table, how its revolutionary macro system solves the "parenthesis problem" without losing Lisp’s metaprogramming superpowers, and write some actual code to see how it feels to build in this exciting new language.

The Core Philosophy: Lisp Power, Modern Syntax

Historically, programming languages have fallen into two camps. On one side, you have languages with rich, readable, infix syntax (like Python, TypeScript, or Rust). The downside is that their ASTs (Abstract Syntax Trees) are highly complex, making user-defined macros incredibly difficult to write. On the other side, you have the Lisp family, where the code *is* the AST. S-expressions make macros easy, but at the cost of visual scanning speed for developers who didn't grow up in a Lisp world.

Rhombus 1.1 bridges this chasm. It uses a modern, familiar syntax with infix operators, object-oriented dot notation, map/list literals, and indentation-based nesting. Yet under the hood, it retains Lisp's "homoiconicity" through a sophisticated parser that represents code as nested groups of "shrubbery" tokens. This allows you to write clean, Python-like code while retaining the ability to rewrite the compiler's behavior on the fly.

Rhombus 1.1 at a Glance

  • Sub-pattern Matching: Sophisticated pattern matching built into the core language bindings.
  • First-Class Classes and Interfaces: Modern object-oriented programming that compiles down to highly optimized Racket structures.
  • Gradual Typing: Support for static-like type annotations that are checked at run-time (and optimized where possible).
  • Advanced Interoperability: Seamless access to the entire Racket ecosystem, including its legendary package manager and concurrency primitives.

Getting Started: Your First Rhombus Program

Let's look at how clean Rhombus looks compared to traditional Lisp-family languages. If you wanted to write a quick function to calculate the Fibonacci sequence in Racket, you'd write this:

#lang racket
(define (fib n)
  (cond [(< n 2) n]
        [else (+ (fib (- n 1)) (fib (- n 2)))]))

Now, let's look at how we write this in Rhombus 1.1. Notice the complete absence of prefix math operators and nested parentheses:

#lang rhombus

fun fib(n):
  cond
  | n < 2: n
  | ~else: fib(n - 1) + fib(n - 2)

// Let's test it
println(fib(10)) // Outputs 55

This looks like a modern, clean, statically typed scripting language. But don't let the clean syntax fool you. Underneath this sleek exterior lies the fully-armed and operational Racket macro system.

Under the Hood: Shrubbery Notation and Macros

How does Rhombus allow you to write macros without s-expressions? In Racket, a macro manipulates a list of syntax objects. In Rhombus, the parser groups tokens into a structure called a Shrubbery.

A Shrubbery is a middle ground between a flat stream of characters and a fully parsed AST. It handles basic groupings like parentheses, brackets, and blocks defined by indentation, but it doesn't assign semantic meaning to them. That semantic meaning is resolved by Rhombus's extensible parser rules. This means *you* can define new syntactic forms that look completely native to the language.

Building a Custom Control Flow Macro

To demonstrate the power of Rhombus 1.1's macro system, let's build a custom control flow structure. Let’s say we want an unless macro. In many languages, you have to write if (!condition). We want to write a native-looking unless statement that executes a block of code only if a condition evaluates to false.

Here is how we define this macro in Rhombus 1.1:

#lang rhombus

expr.macro 'unless $cond:
              $body
            ':
  'if !($cond)
   | $body
   | #void'

// Usage:
decl.macro 'check_threshold $val':
  'unless $val > 100:
     println("Warning: Value is too low!")'

check_threshold(85)  // Prints: Warning: Value is too low!
check_threshold(120) // Prints nothing

Let's break down what's happening here:

  • expr.macro tells the compiler we are defining a new macro that fits into the expression parser context.
  • We use the syntax quote operator ('...') to match and construct syntax templates.
  • The $cond and $body variables are pattern variables. The macro engine automatically parses the code structure and binds these variables to the corresponding shrubbery trees.
  • The macro returns a new syntax block: 'if !($cond) | $body | #void'. This translates our custom syntax directly into standard Rhombus conditionals before execution.

This is hygienically expanded at compile-time. There is no risk of variable name collision (the classic macro hygiene problem) because Rhombus tracks lexical scope through the expansion process, just like Racket.

The Power of Rhombus 1.1 Classes and Interfaces

Rhombus 1.1 refines the object-oriented system, making it incredibly straightforward to define data structures and business logic. Let's look at how we can implement a simple geometric shape system using classes and interfaces, showcasing the clean syntax and type annotations.

#lang rhombus

interface Shape:
  method area() :~ Real
  method describe() :~ String

class Circle(radius :~ Real):
  implements Shape
  
  override area():
    3.14159 * radius * radius
    
  override describe():
    "I am a circle with radius " + radius.to_string()

class Square(side :~ Real):
  implements Shape
  
  override area():
    side * side
    
  override describe():
    "I am a square with side " + side.to_string()

fun print_shape_info(s :~ Shape):
  println(s.describe())
  println("Area: " + s.area().to_string())

// Instantiate and use
let my_circle = Circle(5)
let my_square = Square(4)

print_shape_info(my_circle)
print_shape_info(my_square)

Notice the :~ operator. This denotes a chaperone-based contract check (gradual typing). Rhombus will assert at run-time that the values passed to these methods conform to the specified types. If you try to pass a string where a Real is expected, Rhombus will throw a clear, informative runtime exception, pointing exactly to the boundary where the contract was violated.

Why Should Web and DevOps Developers Care?

You might be thinking, "This is cool, Alex, but I build REST APIs and cloud infrastructure. Why does a Racket-derived language matter to me?"

It matters because domain-specific languages (DSLs) are the ultimate tool for managing complexity. Think about how we write infrastructure today: YAML files for Kubernetes, HCL for Terraform, JSON for cloud policies. These are static configurations that we constantly try to shoehorn logic into.

With Rhombus, you can easily build highly readable, compile-time checked DSLs tailored specifically to your business domains. Want a syntax that lets non-programmers write business rules? Want an internal configuration language for your microservices that is fully validated before a single line of runtime code executes? Rhombus makes building those tools accessible to everyday engineers, without needing a PhD in compiler design.

Wrapping Up: The Future of Extensible Languages

Rhombus 1.1 is more than just a fresh coat of paint on Racket. It is a proof-of-concept that we do not have to choose between human-friendly syntax and developer-empowering metaprogramming. It gives you the raw, unrestricted power of Scheme, wrapped in an elegant syntax that you'll actually enjoy reading and writing on a Monday morning.

If you're looking to stretch your conceptual boundaries this week, I highly recommend downloading Racket, installing Rhombus, and playing around with its unique syntax-matching features.

Over to You!

What are your thoughts on Rhombus? Are you a Lisp traditionalist who loves the parentheses, or does this modern, Python-esque syntax make you want to give the Racket ecosystem another look? Let me know in the comments below!

Until next time, happy coding!

Post a Comment

Previous Post Next Post