When the IDE Edits AST, Not Text

The Tyranny of Text

Every IDE today edits text. You type characters. The editor syntax-highlights them. Maybe it runs a parser in the background to help with autocomplete. But the fundamental unit of editing is text.

The AST is a secondary artifact. Derived. Parse the text, get the AST. Edit the text, re-parse, get a new AST. The AST is never canonical. It's always a projection of text.

But in Yin.vm, the Universal AST is canonical code. Text is just one rendering. What happens when you build an IDE where you edit the AST directly, and text is just a view?

Editing AST as Code: The Core Idea

In an AST-native IDE:

  • The AST is what you edit (the canonical code)
  • Text is a materialized view (one rendering of the AST)
  • Edits modify datoms in the stream
  • The text updates reactively to reflect AST changes

This inverts the traditional model. You're not editing a text file that gets parsed into an AST. You're editing an AST that gets rendered as text.

Implication 1: Syntax Is a Choice, Not a Constraint

If the AST is canonical, syntax becomes a user preference.

The same function can be rendered as:

# Python syntax view
def calculate_total(items: list[Item]) -> int:
    return sum(item.price for item in items)

// Java syntax view
public int calculateTotal(List items) {
    return items.stream()
               .mapToInt(Item::getPrice)
               .sum();
}

;; Clojure syntax view
(defn calculate-total [items]
  (reduce + (map :price items)))

Same AST. Different renderings. You could switch between them with a keystroke. A Python developer and a Java developer could be editing the same code while each seeing their preferred syntax.

Syntax becomes a display preference, like font size or color scheme. The code is the AST underneath.

Implication 2: No More Parse Errors

Traditional editors have a fundamental problem: invalid intermediate states.

When you type:

def calculate

The parser screams. Red squiggles everywhere. The code is syntactically invalid. You can't compile it. Tools break.

But in an AST-native IDE, you never create invalid AST states. You edit the AST through structured operations:

  • Add a function node
  • Set its name to "calculate"
  • Add a parameter
  • Set parameter type

At every step, the AST is valid. It might be incomplete (a function with no body), but it's never syntactically broken. The text rendering might show placeholders for missing parts, but the underlying AST is always well-formed.

Implication 3: Refactoring Becomes Trivial

Traditional refactoring is string manipulation with extra steps. Find all occurrences of a variable name. Replace them. Hope you didn't miss any. Hope you didn't rename something in a comment by accident.

When you edit the AST directly, refactoring is AST transformation:

;; Rename variable
[:find ?node
 :where
 [?node :ast/type :variable]
 [?node :ast/name "oldName"]]

;; Update all datoms
(doseq [node results]
  (transact! [node :ast/name "newName"]))

This is precise. You're not doing string matching. You're querying the semantic structure. A variable named user is different from a string containing "user". The AST knows the difference.

Extract method? Move the AST subtree. Inline function? Replace call sites with the function's AST body. Change signature? Update parameter nodes. All operations are semantic, not textual.

Implication 4: Multi-Language Editing in One File

Because the Universal AST preserves semantics across languages, you could edit different parts of the same codebase in different syntaxes.

Imagine a file where:

  • The data processing pipeline is shown in Python (readable, high-level)
  • The performance-critical inner loop is shown in C++ (low-level control)
  • The configuration logic is shown in Clojure (concise, declarative)

Same file. Same AST. Different renderings for different sections. Each part shown in the syntax that best expresses its intent.

This isn't syntax highlighting. It's syntax multiplicity. The canonical code is the AST. The displayed syntax is a rendering choice.

Implication 5: Time-Travel Editing

Because the AST is stored as datom streams with transactions, every edit is versioned automatically.

Traditional undo/redo is a stack. You can go back one step. But in an AST-native IDE:

  • Every edit is a transaction in the datom stream
  • The entire history is queryable
  • You can branch from any point
  • You can diff any two points in time

Want to see what the function looked like 3 hours ago? Query for transaction tx < 1500. Want to see how the type signature evolved? Query the temporal dimension. Want to merge changes from two different editing sessions? The datoms have full provenance.

This is Git built into the editor, but at the semantic level, not the text level.

Implication 6: Live Collaboration Becomes Natural

Traditional collaborative editing (Google Docs for code) works at the character level. Operational transforms. CRDTs. Complex conflict resolution.

But when you edit AST as datoms:

  • Edits are datom transactions
  • Transactions are immutable facts
  • Conflict resolution happens at the semantic level

Two developers editing different functions? No conflict. Two developers editing the same function body? The datom transactions compose. Two developers changing the same line in different ways? The AST structure makes conflicts semantic, not textual.

Example:

  • Developer A adds a parameter: [fn-1 :ast/params [param-1 param-2 param-3]]
  • Developer B renames the function: [fn-1 :ast/name "calculateTotal"]
  • Both transactions succeed. No conflict. Different parts of the AST.

Implication 7: The IDE Becomes a Database Query UI

If code is a Datalog database, the IDE is a query interface.

Instead of "Find all references," you write:

[:find ?caller
 :where
 [?call :ast/type :function-call]
 [?call :ast/function ?target]
 [?target :ast/name "calculateTotal"]
 [?caller :ast/children ?call]]

Instead of "Show call hierarchy," you write a recursive query. Instead of "Find unused variables," you query for variables with no references.

Every IDE feature becomes a Datalog query over the AST. And because the AST is queryable across five dimensions, you can ask questions traditional IDEs can't:

  • "Show me functions that changed in the last hour and touch network I/O"
  • "Find Python code that became C++ and got slower"
  • "Show me where dynamic data enters static functions without validation"

Implication 8: Type Certainty as Visual Feedback

Because Yin.vm unifies static and dynamic types as certainty metadata, the IDE can show certainty levels visually.

Variables could be colored by certainty:

  • Green: High certainty (declared type, compiler-enforced)
  • Yellow: Medium certainty (inferred type, analyzed)
  • Orange: Low certainty (runtime-discovered)
  • Red: No certainty (unknown, could be anything)

This makes type flow visible. You can see at a glance where high-certainty data becomes low-certainty. You can see the boundaries where validation is needed.

This isn't just highlighting. It's semantic visualization of the type dimension in the datom stream.

Implication 9: The Editor Is Programmable in AST Terms

Traditional editors are programmable with macros, plugins, scripts. But they operate on text.

An AST-native IDE is programmable with AST transformations:

;; Custom refactoring: convert for-loop to map
(defn for-loop->map [node]
  (when (= (:ast/type node) :for-loop)
    (let [body (get-body node)
          var  (get-loop-var node)
          coll (get-collection node)]
      (make-node :map-operation
                 {:function (make-lambda [var] body)
                  :collection coll}))))

You write editor extensions that operate on AST semantics, not text patterns. The extensions compose because they all work on the same canonical AST structure.

Implication 10: Execution and Editing Merge

Because execution state is part of the datom stream (the execution dimension), the boundary between editing and running blurs.

You could:

  • See live values next to variable definitions (like Bret Victor's demos)
  • Edit a function while it's paused mid-execution
  • Query execution history: "Show me all the times this variable was null"
  • Branch execution: "What if this parameter was 10 instead of 5?" and see the AST fork

This is REPL-driven development taken to its logical extreme. The code and its execution are the same datom stream. Editing is adding datoms. Executing is adding datoms. They're both transformations of the canonical AST.

The Challenges

This vision isn't without challenges:

1. Discoverability

Text is familiar. AST editing requires learning structured operations. How do beginners discover them? How do you make AST editing feel natural?

2. Muscle Memory

Programmers have decades of muscle memory for text editing. Switching to AST editing means unlearning those habits.

3. Rendering Ambiguity

Some AST structures don't map cleanly to text. How do you render a partially-evaluated expression? A type-level computation? Not all ASTs have obvious text representations.

4. Performance

Querying a datom database on every keystroke could be slow. The IDE needs incremental updates and clever caching to stay responsive.

The Precedent: Scratch, Blockly, and Visual Programming

We've seen hints of this in visual programming:

  • Scratch edits a block-based AST, not text
  • Blockly does the same for web apps
  • Unreal Engine Blueprints for game logic
  • LabVIEW for instrumentation

But these are limited to narrow domains. They lack the universal semantic layer that Yin.vm provides. They can't span languages. They can't query across time. They don't preserve execution state.

An AST-native IDE for Yin.vm would be Scratch for professional programming, but with the full power of a queryable, multi-dimensional datom stream underneath.

Conclusion: Code Is Not Text

The fundamental insight: code is not text. Code is a semantic structure. An AST. A stream of immutable facts describing computation.

Text is just one serialization format. A rendering. A view.

When the IDE edits the AST directly:

  • Syntax becomes a preference, not a constraint
  • Refactoring becomes precise semantic transformation
  • Collaboration happens at the semantic level
  • Time-travel and branching are built in
  • The entire codebase is queryable as a database
  • Type certainty becomes visible
  • Execution and editing merge

This isn't a better text editor. It's a fundamentally different model of programming. One where the canonical form of code matches how we think about it: as structure, not as characters.

This is what becomes possible when the Universal AST is canonical code.

Learn more: