DEV Community

kargathara Aakash
kargathara Aakash

Posted on

SkyHetu: Designing a Causality-First Programming Language in Rust

SkyHetu
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:

  1. Immutability by Default: You can’t change a variable unless you explicitly ask for it.
  2. Explicit State: Mutable variables are declared with state, not let.
  3. The Arrow Operator (->): Assignment (=) is for initialization. The arrow (->) is for mutation.
state counter = 0
counter -> counter + 1
Enter fullscreen mode Exit fullscreen mode

This isn't just syntactic sugar. That -> operator does two things:

  1. Updates the value.
  2. 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>,
}
Enter fullscreen mode Exit fullscreen mode

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"))
Enter fullscreen mode Exit fullscreen mode

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:

  1. Enums: Representing dynamic types (Value::Number, Value::string) is trivial and safe.
  2. Performance: The overhead of tracking causality is non-zero, but with Rust’s zero-cost abstractions, I could optimize the critical path.
  3. 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
Enter fullscreen mode Exit fullscreen mode

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)