SkyHetu: Designing a Causality-First Programming Language in Rust
Source: Dev.to

Most bugs I’ve encountered in my career boil down to one question: “How did this variable get here?”
Modern programming languages are amnesiacs. If user.balance is 0, the application knows it is 0 now, but it has no memory of the sequence of events that led to that state. Was it a logic error? A race condition? A rogue function call? To find out, we add print statements, attach debuggers, and try to replay history in our heads.
I decided to build something different. I built SkyHetu, a programming language where Causality is a first‑class citizen.
The Philosophical Shift
In SkyHetu, you don’t just change state; you create an event. To achieve this, I enforced three radical design decisions:
- Immutability by Default: You can’t change a variable unless you explicitly ask for it.
- Explicit State: Mutable variables are declared with
state, notlet. - The Arrow Operator (
->): Assignment (=) is for initialization. The arrow (->) is for mutation.
state counter = 0
counter -> counter + 1
The -> operator does two things:
- Updates the value.
- Logs the causality.
Building the Time Machine in Rust
To make this work, I needed performant, low‑level control over memory and execution flow, so I chose Rust. The core of SkyHetu is a stack‑based virtual machine (VM). Alongside the stack and the heap sits the CausalityLog.
pub struct CausalityLog {
history: HashMap>,
clock: usize, // Logical time
}
pub struct MutationEvent {
pub old_value: Value,
pub new_value: Value,
pub timestamp: usize,
pub location: Option,
}
Every time the VM executes the OP_TRANSITION instruction (triggered by ->), it captures a snapshot of the before and after values, stamps it with a logical clock tick, and pushes it into the history.
The “Detective Board” Visualization
Because the language remembers everything, debugging feels less like archaeology and more like detective work. I implemented a native function called causal_graph() that exports the internal history into DOT format (for Graphviz) or JSON.
state score = 100
score -> score - 10 // event 1
score -> score - 50 // event 2
print(causal_graph("score", "dot"))
The generated graph shows each state as a node and each timestamped transition as an edge—a “Detective Board” for your code.
Why Rust?
Rust was the perfect tool for this for three reasons:
- Enums: Representing dynamic types (
Value::Number,Value::String) is trivial and safe. - Performance: The overhead of tracking causality is non‑zero, but Rust’s zero‑cost abstractions let me optimize the critical path.
- Correctness: Writing a garbage collector and VM is complex. The borrow checker saved me from countless segmentation faults that would have plagued a C++ implementation.
The Result
SkyHetu isn’t just a toy; it compiles modules, handles closures, and even supports classes. Unlike other languages, it offers introspection built‑in. You can ask the language: “Why is X true?” and get a causality chain.
print(why(counter))
// Causality chain for 'counter':
// 1. [t=1] 0 -> 1
// 2. [t=2] 1 -> 2
We spend 90 % of our time debugging. Maybe it’s time our languages helped us with the other 10 %.