AEON began as a practical attempt to make typed data easier to move through applications. It became a long argument with syntax, semantics, representation, value, trust, and the question of who is allowed to decide what data means.
AEON is a notation language eighteen years in the making, built on the lessons from other formats of its era. I set out to solve a practical problem: how do I apply custom types inside an object notation language? I ended up somewhere much stranger. That is quite a mouthful. It was also quite the journey.
I was a Flash developer in the early 2000s. XML was all the rage, and while it had solved a host of problems when it came to ingesting data from external sources, it also introduced a new set of inconveniences.
While its verbosity did not help, the real issue was that it was a pain to use. Especially if all you wanted to do was transport application data from a server or file to a client or app. XML could express the structure of your data, but not what it was with any clarity. At least not out of the box. One had to build a convention or language on top of it to ensure no ambiguity crept in. The biggest pain point was simply traversing an XML tree, which made validation hard. Possibly the first parser I ever built was to make this process a lot smoother.
Despite that, I still found that on any project I was working on, I was spending an exorbitant amount of time just creating XML data structures and making sure they were properly validated. Inevitably, one had to ask: there must be a better way?
I was not the only one to ask this question. In fact, most formats that saw the light of day during the early 2000s can likely be traced back to being a reaction to XML. It is not that XML is bad. It was just the wrong tool for most jobs.
I looked into JSON, a language that famously was not invented but discovered, and it looked promising. It was less verbose than XML and, at the very least, had a basic set of inferred types. The data structure also mapped more cleanly to the data types used in a programming language.
The language permissive parsers taught me
Unfortunately, I ran into two problems. The first had to do with Postel's Law: "Be conservative in what you do, be liberal in what you accept from others."
This simple principle allowed the web to grow as fast as it did, but it created a lot of compatibility debt in the process.
Compatibility debt is the cost of preserving yesterday's tolerances.
HTML was the first place I really felt both sides of that bargain. You could mangle the code, leave it full of mistakes, and the browser would still try to infer what you meant. It felt generous: technology working around human imperfection rather than nagging at every slight mistake. It was also the beginning of a long bill.
The same spirit made its way into JavaScript through coercion rules intended to make the language friendlier. Ironically, it made reasoning harder. The rules may be internally consistent, but application flow becomes full of hidden conversions, bugs, and gotcha moments.
JSON inherited some of that world, but with the twist of being intentionally tiny. The specification famously fit on a business card. That restraint was valuable, but it also meant that many questions a notation ecosystem eventually has to answer were left outside the room.
Back to my thread. I set to work trying to get JSON to work inside Flash. Unfortunately, I ran into two unexpected problems. The first was not with JSON itself, but with the tools surrounding it.
Up until that point, I had assumed the following was perfectly valid JSON:
{
key:"value"
}
The browsers and online tools I had been using accepted it without complaint. They quietly corrected my mistakes and carried on. When I attempted to use the same data in Flash, it failed immediately.
It turned out Flash was correct. Valid JSON required this:
{
"key":"value"
}
I had unknowingly learned JSON through permissive parsers rather than through the specification itself. In hindsight, this was my first real encounter with the downside of Postel's Law. The parsers had been liberal in what they accepted, but in doing so they had taught me a language that did not exist.
If I had to build a parser anyway
My troubles did not end there. At the time, the only way to evaluate JSON in Flash was through eval. None of my code ever touched eval, because it allows arbitrary code to run inside your app at runtime. There was no way I would make that the front door for outside data. If I wanted JSON, I would have to build a parser.
Flash itself was moving toward stronger typing. ActionScript 2 had introduced a type system, and ActionScript 3 compiled directly to bytecode on top of ideas from the ECMAScript 4 proposal. Types and classes were in the air. Browsers hesitated; Flash adopted them.
And that got me thinking. If I was going to build my own parser anyway, I might as well create my own language that supported my own needs. Something that supported types. AEON.AS was born.
One of the issues I quickly ran into was that if I was going to define custom types, there was no way for the parser to know what a custom type was, let alone turn it into a runtime object. Fortunately, as I had access to all the code, I could build in hooks, allowing the app to intervene and instruct the parser what to do before materialization. The fundamental principle that drives almost everything else about AEON was born here.
The parser does not own meaning. The app does.
Once I realized I could hook into the parser and tap into its event stream, I also understood that I could move validation forward. Rather than traversing the final object tree, I could inspect every event being fired one by one. This necessitated an addressing system, and now each AEON.AS document basically had its own internal DOM.
While AEON.AS was still very much tied to the AS3 runtime, it did solve my initial problem: the amount of time and care I had to spend importing and exporting data out of my apps.
Then Flash disappeared
Most things rarely happen in a vacuum, and this was no exception. A year before I developed AEON.AS, Steve Jobs unveiled the iPhone. The device that was meant to be the future quickly raised the question: why did it not run Flash? A few years later, the answer became clear. Flash was dead. In less than five years, the internet was cleansed, and my little language was left to collect dust.
XML lost its dominance and, in its wake, was replaced by formats like JSON, YAML, and Markdown. Each one answered the question "there must be a better way" in its own way. Returning to vanilla JS with proper JSON support, there was no need for AEON. But unlike Flash, it lived on in notes and sketches I kept throughout the years on how to improve the language should I ever resurrect it.
Eighteen years later
Almost eighteen years later, the landscape had shifted again. The obvious conclusion is that the rise of typed languages such as TypeScript brings back the need for something like AEON. The change, however, goes much deeper.
The most important aspects of modern life now exist in digital form. Contracts, identities, finances, medical records, infrastructure, and increasingly even decision making itself are mediated through software. With so much at stake, "be liberal in what you accept" is no longer merely a convenience. It is a risk.
At the same time, we began entrusting ever larger portions of this digital world to systems that do not reason deterministically, but probabilistically. Systems that excel at producing plausible interpretations rather than verifiable facts.
For the next version of AEON, I made some early decisions that would guide the process:
No execution, no processing. In this day and age, that is a security risk. Keep custom types. Keep the addressing system. Keep the syntax and grammar simple. No modes if possible. Be platform agnostic.
Sifting through my notes on what to keep and what to throw out, it dawned on me that the real complexity did not live in syntax. It lived in types, author intent, consumer understanding, and what a parser is actually allowed to know.
As programmers, we love assumptions. HTML adds a missing tag rather than throwing an error. Excel assumes 20081102 is a date instead of a number. Convenient when it works; annoying at best when it does not; corrupted data at worst.
If I had been targeting one programming language, I would have inherited its logic. Once AEON became platform agnostic, every convenient assumption had to be questioned. What is a number? What is a null value? What even is a Boolean? True and false, yes, obvious. But what about "true" and "false", or 0 and 1?
Representation, value, and meaning
Once you start asking those questions, you end up unwrapping the layers most languages collapse: representation, value, and meaning. I remember sitting in philosophy classes, hearing about syntax and semantics, and wondering why I would ever need this. Years later, while trying to map the meaning of Not a Number through a processor pipeline, I wished I had paid more attention.
Convenient systems collapse these layers because that is what programmers usually want. Read the file. Give me an object. Let me get on with my life. But every collapse hides a decision. Duplicate keys are a simple example: some implementations reject them, some keep the first, others keep the last. Most of the time nobody notices. Then an edge case appears, and the output is not what you thought you agreed to.
The mantra that kept coming up was: whatever is happening here, is it explicit? For AEON to remain honest, it could no longer be just a language. It had to become a system where every stage asks the same question: what do I know, and what must I defer?
That is why AEON looks stretched out. It moves steps that usually happen after materialization to before it. Each layer does one thing. The lexer checks syntax. The schema checks form, and only form. Meaning waits. A nice side effect is security: faulty data can be rejected while it is still inert, before it has entered runtime code.
Take the following statement:
meaningOfLifeTheUniverseAndEverything:ultimateAnswer = 42
The parser can check whether it is syntactically correct. The parser can also check whether the grammar works. The parser, however, does not know what ultimateAnswer is. It can infer that the value is a number, but only the schema can say whether that is the expected representation. The parser also cannot tell you whether 42 is true. That needs to be deferred to a layer that understands the context and meaning of the claim.
AEON contains nouns. The application contains verbs.
That brings us to the question that eventually became central: what does it mean to realize meaning? Why is a temperature statement in a notation language fundamentally different from the same statement inside a programming language?
temperature:number = 32
The answer lies in AEON's non-execution rule. The data it carries is inert. It can describe something, but it cannot do anything.
The parser can determine that 32 is a number. A schema may determine that it is a valid temperature representation. Neither can determine whether it is dangerous, whether an alert should be triggered, or whether the value is Celsius or Fahrenheit. Those decisions belong to a system that understands the context in which the claim exists.
Or put another way: AEON contains nouns. The application contains verbs.
temperature:number = 32
if (temperature > 30) {
triggerAlert();
}
Separately, neither possesses complete meaning. One describes the world. The other describes how to react to it. Meaning is realized when the two meet.
This is why AEON can feel strange at first. At each step, it asks only what it knows, then preserves what later stages may need. Types are not nullable except through an explicit null literal. Numbers skirt IEEE 754 because IEEE 754 is a runtime dependency, and NaN is not a number, so AEON is not going to pretend it is.
Reject or preserve. Do not collapse.
Rather than accepting this:
temperature:number = NaN
AEON makes it explicit by having its own NaN literal:
temperature:nan = NaN
At the schema level, the consumer decides whether temperature can accept NaN or null values, or whether it should only accept actual numbers. Schema is evaluating the representation of NaN, not the meaning of NaN. Anyone who has programmed in JS knows why that distinction matters: NaN == NaN and NaN === NaN both return false.
The rabbit hole does not end there. Take the following:
temperature:number = 0
It has a zero value. Nothing special until we look at the next example.
temperature:list<number> = [ 0.0, 000, 0e0, -0, 0.0e0, 0.00, +0 ]
Anyone reading this will know these are all representations of the same value: zero. Yet each value is also hiding extra information inside it. Sometimes these distinctions matter, sometimes they do not. The parser cannot know. It should either reject or preserve, not collapse them back to 0.
Most languages leave these kinds of decisions undefined, and in most situations it probably does not matter. Until you require -0 to be preserved and it works in your implementation, but silently gets collapsed to 0 in a different implementation in the wild.
AEON cannot afford this. If two implementations do not agree, its canonical representation layer fails, making it pointless. Ironically, for a parser that does very little, the specs have to detail every edge case and how to handle it. It is a lot of work, but there are plenty of examples of what happens when a notation language skips this step. The ecosystem fragments, or the ambiguity becomes part of the specs over time.
The development process felt like pulling a thread, revealing assumptions that had to be unknotted, only to find more threads underneath. With each assumption challenged, a language emerged and slowly evolved into a system, every piece asking the same question: do I have the authority to say anything about this? It just so happens that this also aligns neatly with zero-trust principles.
The result is that AEON does not have features. It only has consequences.