This is the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

News About Bramble

Bramble and Thorn

Documenting the development of a new programming language

Origin

Bramble began as a personal challenge to build a compiler as a personal challenge and endurance exercise. But I really enjoyed this particular work, especially how much tedious knowledge and work it seems to require. And now, Bramble has become my attempt to test out many ideas I have had about programming languages over the years and to see if they are good or bad.

Amplification Through Abstraction

Amplification is what I call the design ethos behind Rust’s lifetimes feature. Lifetimes are a semantic representation of the stack frames that CPUs physically create in order to mechanically execute the code you write. Through the lifetime abstraction, Rust amplifies rather than elides the mechanical nature of the stack and stack frame.

Lifetimes have been around for as long as computer programs have had variables. In languages like C, the lifetime are implicit: there is nothing to tell you not to return a pointer to a local variable or to detect that a structure on the heap contained a pointer to a local variable. These are just things that you had to learn by doing and failing. The lifetimes of variables are communicated to you implicitly. In garbage collected languages, everything that is passed by reference is put on the heap and lives for as long as it has referrers: for the developer, all lifetimes are infinite and, therefore, do not exist. But, in GC languages, lifetimes still existed in physical reality.

Rust, however, made the lifetime an explicit part of the language: something that you had to specify in your code and which the compiler would then check and validate. Now, the language and the compiler were explicitly communicating how the CPU and stack work in the physical world. Rather than elide the physical reality of the CPU, Rust uses an abstraction to amplify it and causes several benefits. First, the compiler can validate that you do not use a variable after its lifetime. Second, you can explicit document lifetime requirements in your code. Third, Rust actively trains inexperienced users to understand lifetimes and stack frames and consciously think about how the CPU mechanically operates when executing their code.

This inspired me to ask how much more could be done by using amplification rather than elision as a goal of abstraction in language design. Which lays down the core design philosophy of Bramble: to amplify the physical nature of CPUs and make their thorny natural reality something that a developer actively interacts with and acknowledges in the code that they write.

The first feature that will demonstrate this design principle will be semantic tools for defining consistency in your code. The focus on the next few blog posts will be on the exploration and design of this feature.

From Implicit To Explicitnew

The corollary of amplification is “things which are present but were implicit, can now be made explicit”. In Rust, the existence of lifetime semantics the compiler checks, also means the existence of semantics that you can use to explicitly define the lifetime requirements of types and functions that you write. And this, in turn, communicates some information to readers about how your code interacts with memory.

The use of amplifying designs in Bramble will be a failure if they do not, simultaneously, lead to elegant tools for a developer to explicitly capture the logical intent of their code in way that means a junior reader of their code will understand even incredibly subtle logic.

Thorn

My desire for amplification is not solely about the language, it also shines upon the compiler itself as a user facing tool. Compilers are incredibly complex pieces of software, and yet, what they do and why they do it is simple. My other big experiment with Bramble is to see what happens if a compiler were as transparent as possible with every single decision and action it took.

Thorn is the first realization of that goal. It is the insights platform that goes hand-in-hand with the compiler and gives complete insight into everything the compiler does and then allows users to visualize and interact with those decisions to better understand how the code they write becomes the instructions that a CPU follows.

A huge inspiration for Thorn is Compiler Explorer and wanting to see what tools could be built if Compiler Explorer were built into the compiler from the ground up and covered every inch of the Bramble compiler’s logic.

Status

Currently, Bramble is in an early alpha state in terms of language features. The focus has been on these goals

  1. Implementing basic primitive types and aggregate types.
  2. Setting up the features necessary for FFI with C.
  3. Integrating Thorn insights infrastructure into the compiler.

FFI support for C is a major first stage requirement because it lets me use C library functions for everything that has not yet been implemented in Bramble (e.g., IO, memory allocation, files, etc.).

Integrating the infrastructure for Thorn is also a critical requirement so that every feature is built with the conscious goal of making sure it integrates with the insight system. If Thorn is a first-class feature then it must be treated as such from the beginning.

I am wrapping up work on the C FFI support and the basic primitives.

The Thorn infrastructure has been fully integrated into the Bramble compiler and every new feature must include how it will leverage Thorn as part of its design.