
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
This isn't just syntactic sugar. That -> operator does two things:
- Updates the value.
- Logs the causality.
Building the Time Machine in Rust
To make this work, I couldn't just write an interpreter in Python. I needed performant, low-level control over memory and execution flow. I chose Rust.
The core of SkyHetu is a stack-based Virtual Machine (VM). Inside the VM struct, alongside the stack and the heap, sits the CausalityLog.
pub struct CausalityLog {
history: HashMap<String, Vec<MutationEvent>>,
clock: usize, // Logical time
}
pub struct MutationEvent {
pub old_value: Value,
pub new_value: Value,
pub timestamp: usize,
pub location: Option<String>,
}
Every time the VM executes the OP_TRANSITION instruction (triggered by ->), it doesn't just overwrite memory. It captures a snapshot of the before and after, 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 this 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"))
This generates a graph where every node is a state, and every edge is a timestamped transition. It’s 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 with Rust’s zero-cost abstractions, I could optimize the critical path.
- Correctness: Writing a Garbage Collector (GC) 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. But unlike other languages, it offers introspection built-in.
You can ask the language: "Why is X true?"
And with the function why(x), it answers.
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%.
Check out the source code on GitHub: https://github.com/Kargatharaakash/skyhetu
Top comments (0)