Design Philosophy

Overview

Precedent is a data-oriented and procedural systems programming language. The language is built around explicit structure, predictable storage, and deterministic lifetime. Its design is inspired by the Wirthian tradition of readable programs, strong structure, and clear data modeling, but it is informed by modern concerns: cache locality, predictable performance, SIMD-friendly data, safe resource ownership, cross-platform native code generation, and data-oriented programming.

Precedent is not Pascal.
I want to be clear that while Precedent looks a lot like many modern variants of Pascal, at a surface level, due to borrowing a lot of Pascal syntax, it is quite a distinct language from what Pascal is generally understood to be today. The compiler has an entirely new type system and entirely different storage semantics.
Instead, Precedent is best described as a “spiritual successor to Wirthian-era Pascal.” While modern successors to Pascal already exist, they were born of very different goals and objectives. Precedent is my answer to a simple question : “What would happen if we rolled back the Pascal language to around the 1970s, and then rolled it forwards again with hindsight.”

Precedent is intentionally not an object-oriented language. It does not use class inheritance, virtual method tables, ordinary object references, or heap-allocated object graphs as its primary abstraction model. Instead, it provides a set of structured language features providing many of the benefits that programmers often seek from object-oriented languages, while avoiding many of the costs. Precedent is a data-oriented procedural programming language, which attempts to resolve the ergonomic constraints that are typical of existing procedural languages.

The following sections explain the major design decisions behind that choice: data-oriented procedure, lexical ownership, compiler-known storage forms, explicit interop boundaries, integrated tooling, and predictable lowering.

Data-Oriented Procedural

Precedent is not a flat rejection of object-oriented programming. It is a re-evaluation of OOP’s strengths and weaknesses.

The traditional four pillars of object-oriented programming are usually described as abstraction, encapsulation, inheritance, and polymorphism. Of these, Precedent directly rejects inheritance as a core language feature. It does not reject abstraction. It does not reject encapsulation. It does not reject the need to represent different forms of data under a common concept. What it rejects is the inheritance tree as the default mechanism for organizing programs.

There are reasonable academic arguments both for and against inheritance. Is taxonomical classification actually as intuitive as it first appears? Is an object really the right boundary for clean abstraction? Does inheritance-based abstraction scale well as software grows? The author of Precedent has strong views on these questions, but the compiler does not depend on those views.

Precedent rejects inheritance for two more direct reasons.

First, performance-sensitive software has often moved away from inheritance-heavy designs in its most critical paths. Video game engines, real-time simulations, embedded firmware, compilers, and other systems software frequently favor procedural or data-oriented approaches, especially where predictability and locality matter. The reason is not merely philosophical. Inheritance-heavy object models often bring real costs: virtual dispatch, pointer chasing, fragmented heap allocation, scattered object layout, and reduced cache locality. Modern hardware can hide these costs in many cases, but it does not erase them. They still exist, and in performance-critical systems they can matter.

Second, large application software often degrades in maintainability over time. After decades working in application development, I have seen this pattern repeatedly: a code base begins with clear structures and clean intentions, but as requirements accumulate, it becomes increasingly difficult to reason about. Refactoring or rewriting large sections of code is often proposed as the remedy, but in commercial software this is expensive, risky, and frequently unsuccessful. If a program became hard to maintain because its structure failed under accumulated complexity, rewriting the same complexity does not automatically produce a better structure.

Precedent’s view is that object-oriented programming is often a willing participant in this decline. It is not always directly responsible, and inheritance is not the only cause, but OOP makes certain kinds of structural decay very easy. Programs may begin with well-defined data structures: lists, trees, dictionaries, graphs, and so on. Over time, edge cases are handled by adding references between objects, subclassing existing types, or introducing special-case inheritance relationships. Each individual change may seem small and reasonable. But as those changes compound over years, the program’s original structure becomes less visible.

A tree is easy to reason about when its relationships are parent and child. But the moment a node in that tree contains a reference to another node for a reason unrelated to the tree’s hierarchy, the structure has become more irregular. The program is no longer simply a tree. It is now a tree plus an extra relationship. Add enough of those relationships over time, and the program becomes an irregular graph of references. That graph may not be named, documented, or centrally managed. It simply emerges.

Precedent is designed to discourage this kind of accidental structure.

It does this by rejecting inheritance and by not providing ordinary reference types as a language default. Instead, relationships are expected to be represented as explicit data structures. If the program needs a hierarchy, use a tree. If it needs connectivity, use a graph. If it needs lookup, use a dictionary. If it needs stable identity, use handles. If it needs heterogeneous variants under a shared concept, use a plex. The goal is not to forbid complexity. The goal is to make complexity visible.

Precedent is inspired by Niklaus Wirth’s formulation, Algorithms + Data Structures = Programs, and by the simple idea that code is code and data is data. Algorithms matter, but algorithms operate on data. If the data is poorly shaped, hidden behind references, or scattered across a heap of loosely connected objects, the program becomes harder to understand and harder to make fast.

Precedent therefore favors composition over inheritance. It provides clear declaration of data structures, predictable ownership, and procedural execution flow. It then provides a way to combine data and code without adopting class inheritance.

Where OOP uses classes to bind data to the code that operates on that data, Precedent provides the abstraction. An abstraction is a namespace and storage boundary for data and the functions that operate on that data. It allows related procedures to preserve invariants and manage internal structure without requiring inheritance, virtual method tables, or object reference graphs.

Lexical Ownership

In Precedent, storage is owned by the declaration that creates it. A variable owns its storage within the scope tree, and when that scope ends, the storage is released or finalized according to the rules of its type. This approach is a rejection of two common memory-management models as defaults: garbage collection and automatic reference counting.

Garbage collection can be highly productive, and many garbage-collected runtimes are impressive pieces of engineering. However, garbage collection also introduces a runtime memory manager whose behavior is not always directly controlled by the programmer. In performance-sensitive software, this can create concerns around pause times, scheduling, memory pressure, and frame-to-frame consistency.

Automatic reference counting is more deterministic, but it encourages reference-oriented programming directly. One of the clearest demonstrations of the problem is the weak reference. Weak references exist largely because strong reference counting alone cannot naturally handle cycles. If entity A strongly references entity B, and entity B strongly references entity A, then neither entity’s reference count can reach zero. The system needs some way to break or avoid the cycle. Weak references provide that mechanism, but they also introduce additional conceptual and runtime machinery.

The more important question is: why did the developer introduce the cycle in the first place?

Sometimes there is a legitimate reason. But often the answer is that reference-driven programming makes it too easy. A program may not contain an obvious cycle such as “A references B and B references A.” Instead, the cycle may be spread across many layers of architecture: views, models, services, callbacks, observers, caches, delegates, and event handlers. The cycle becomes difficult to see because the program’s structure is no longer represented by explicit data structures. It has emerged from a web of references.
Precedent tries to avoid making that web the default.

This led to a question I asked myself while learning to program: why do I need to new and free things at all?

I never need to manually allocate or release ordinary intrinsic variables such as integers or scalars. They exist because I declared them. They are initialized according to the language rules. They disappear when their scope ends. At first, the answer seems obvious: small intrinsic values are usually stored directly in local storage, while larger dynamic entities often live somewhere else, commonly in heap memory, with only a reference stored locally. But many languages already manage some non-trivial values by scope. Arrays, strings, records, and other compound values may involve storage that is larger or more dynamic than a simple integer, yet the programmer does not always manually allocate and free them. The variable represents the value, and the language or runtime manages the storage according to its rules.

Precedent extends this idea further.

If a list, tree, graph, ring, stack, dictionary, plex, or abstraction is declared as a variable, then that declaration owns the storage. The programmer does not normally allocate it with new, and does not normally release it with free. The value exists because it was declared. It exists in one owning location. When that owning scope ends, the compiler emits the required cleanup for that storage. The deeper reason this is possible is that Precedent does not make general reference programming the default model.

In many object-oriented languages, objects are created through ad-hoc heap allocation and manipulated through references that can easily cross scope boundaries. This makes it natural for object lifetimes to become detached from lexical scope. Once object lifetimes freely transcend the scope tree, the language needs some other mechanism to decide when the object should die: manual free, garbage collection, reference counting, ownership annotations, or some other system.

Precedent starts from a different assumption:

This thing exists because you declared it, and it exists only once at the place you declared it.

Other code may borrow that storage through parameter passing. Internally, the compiler may implement this borrowing by passing an address or reference, and in practice it often will. But this is an implementation detail. The programmer is not expected to manage that reference as a value. The programmer is encouraged to think in terms of passing the thing itself under a declared access mode.

This preserves a simple model:

  • declarations own storage.
  • assignment copies values.
  • parameters borrow storage.
  • scope controls lifetime.
  • unsafe pointers are explicit exceptions.

The practical result is that ordinary Precedent storage cannot be leaked by forgetting to free it. A declared collection, abstraction, or structured value is cleaned up according to its type when it leaves scope. If it owns external resources, finalization can release them. If it contains other finalized or no-copy values, those rules propagate through the type system.

Precedent does not forbid lower-level memory management. If a program needs an unsafe pointer, an ad-hoc buffer, a foreign allocation, or a custom page allocator, it can use one intentionally. But that is no longer the default path of the language. It is an explicit escape from the ordinary ownership model.

Precedent’s default memory model is therefore not garbage collection, not automatic reference counting, and not manual heap allocation. It is lexical ownership: storage belongs to the declaration that created it, and lifetime follows the scope tree.

No General-Purpose Heap

I admire many alternative compiler and language projects currently under active development, particularly those designed around explicit procedural behavior and strict memory management. Languages such as Rust, Odin, Zig, Jai, and others are clearly tackling the problems of object lifetime and memory management in serious and interesting ways.

However, many modern approaches seem to fall toward one of two broad positions.

One approach provides a reference or ownership management system, with borrowing, sharing, lifetime, or reference-counting semantics. Another approach moves strongly in the opposite direction and makes memory management an explicit manual concern for the developer, often through allocator parameters or explicit allocation and release operations.

Precedent is an attempt to find a different middle ground.

As discussed in the previous section, Precedent manages individual value lifetimes through lexical ownership. Storage is owned by the declaration that creates it, and that storage is released or finalized when the declaring scope ends. But lifetime is only one part of the memory problem. Another major concern is allocation shape.

Traditional general-purpose heaps are flexible and sophisticated, but they are also general. They allocate memory according to what is available at the time of allocation, using internal policies designed to serve many different allocation sizes and lifetimes. This flexibility has a cost. It can lead to fragmentation, allocator bookkeeping overhead, unpredictable allocation behavior, and scattered data layout.

Fragmented data layout is particularly important for performance. If related data is scattered across memory, the CPU must spend more time waiting on memory. Cache locality suffers. TLB behavior may suffer. Predictable iteration becomes more difficult. Even when the allocator itself is highly optimized, the program’s access pattern may remain poor.

The common alternative is to let the programmer choose allocators explicitly.

For example, a program may allocate certain objects from an arena, a slab, a temporary allocator, a page allocator, or some other custom allocation strategy. This is powerful and often necessary in systems programming. But it also pushes allocator mechanics into ordinary code. Functions must accept allocator parameters. Data structures must store allocator references. Call sites must decide which allocator to use. The programmer must constantly reason not only about the data, but also about the allocation mechanism behind the data.

This is not inherently bad, but it has an ergonomic cost.

Some languages reduce that cost through a scoped or contextual allocator. A current allocator may be placed into a context and used as the default until changed. This improves ergonomics, but the programmer is still thinking directly in terms of allocators.

Precedent takes a different approach.

Precedent does not normally ask the programmer to select an allocator. Instead, it asks the programmer to select a storage form.

If the data must be contiguous, use an array.
If the data needs stable reusable slots, use a list.
If the data is hierarchical, use a tree.
If the data is connected, use a graph.
If the data is cyclic, use a ring.
If the data is lookup-oriented, use a dictionary.
If the data is last-in-first-out, use a stack.

In Precedent, these collection types are compiler intrinsic types. They are not merely runtime-library containers. The compiler understands their structure and synthesizes the code necessary to manage them. This gives the compiler opportunities to inline common operations, emit specialized fast paths, enforce type and lifetime rules, and apply consistent storage behavior.

More importantly, each collection type implies a memory management strategy.

Internally, Precedent’s compiler-managed collections are intended to lower to a small number of allocation patterns. At the lowest level, they require a page provider. Above that, collection storage can be built from slabs and fanning directories. The details remain compiler-managed, but the programmer’s choice of collection determines the storage behavior.

An array represents contiguous storage. It is the appropriate choice when cache-friendly iteration, indexed access, SIMD-friendly processing, or thread slicing is important. The cost of this choice is that insertion and removal may require moving elements, and growth may require reallocation to preserve contiguity.

A list represents reusable slot storage. It is the appropriate choice when items need stable identity, frequent insertion and removal, and handle-based access. A list can use internal free-list management and generational handles to reuse storage safely. The cost of this choice is that iteration and access may not have the same locality guarantees as a contiguous array.

A ring represents fixed cyclic storage. It is appropriate for event queues, logs, recent history, audio buffers, input history, and other bounded circular workloads.

A tree represents hierarchy. A graph represents connectivity. A dictionary represents key-based lookup. A stack represents scoped last-in-first-out work.

The programmer is therefore still making performance-relevant decisions. Precedent is not hiding memory behavior. It is raising the decision to the level of data structure rather than allocator mechanics.

The guiding principle is:

Do not ask first, “Which allocator should I pass?” Ask first, “What shape should this data have?”

Once the programmer answers that question, the compiler can choose the appropriate storage strategy for the selected collection type.

This approach preserves control without making allocator plumbing part of ordinary code. It also helps keep programs structurally honest. A program’s data layout is visible in its declarations. The chosen collection communicates intended access patterns, lifetime behavior, and performance tradeoffs.

Precedent does not forbid explicit memory management. Unsafe pointers, custom buffers, custom page allocation vectors, and low-level runtime support remain possible when needed. A freestanding target, for example, may provide its own page allocation functions before compiler-managed dynamic collections are used.

But explicit allocator work is an intentional escape hatch, not the default programming model.

Precedent’s default is not a general-purpose heap and not allocator-passing ceremony. Its default is compiler-known storage forms, lexically owned by declarations, lowered to predictable allocation strategies.

Assignment Means Copy

In Precedent, a variable identifier is not a reference to something elsewhere. It is the symbolic name for a specific declared value.

When a value is assigned to a variable, the value is copied into the storage owned by that variable. Assignment does not rebind the variable to another object. Assignment does not make two variables refer to the same storage. Assignment copies.

This is easy to understand for simple intrinsic types, because it matches the behavior programmers already expect from integers, floats, and similar values. If one integer variable is assigned to another, the integer value is copied.

Many languages change this rule for more complex types. In reference-centered languages, assigning one object variable to another usually does not copy the object. It copies the reference. After the assignment, both variables may refer to the same underlying entity.

Precedent does not use that model.

In Precedent, assigning one entity to another means that the value of the first entity is copied into the second. At first, this may feel counterintuitive to programmers familiar with reference-based languages, but it provides an important guarantee: a variable remains the name of its own declared storage.

A variable does not drift from one heap object to another over time. It does not become an alias for another variable’s storage. Its declaration establishes its ownership location within the scope tree, and that ownership remains stable.

For simple types, this is straightforward. For complex types, the same rule still applies, but the consequences are larger.

Assigning one list to another list of the same element type means copying the list’s contents. Assigning one abstraction to another means copying its copyable fields. Assigning one plex to another means copying the common fields and the active variant payload. Assignment is therefore potentially a deep operation.

This is powerful, but it must not be invisible.

It would be easy to accidentally assign one large collection to another and cause an expensive deep copy. For this reason, the compiler will warn when ordinary assignment would copy a deep structure. Precedent also provides explicit syntax to state that the deep copy is intentional.

The intent is not to forbid copying. Copying is sometimes exactly what an algorithm requires. A program may need a snapshot, a shadow copy, a working copy, or a preserved previous state. In those cases, an explicit deep copy is useful and appropriate.

The intent is to prevent expensive or semantically significant copies from happening casually.

In practice, this rule changes how the programmer thinks. Once variable identifiers are no longer treated as references, they begin to feel like discrete entities. If the program already has access to an entity, can pass it to procedures, and can mutate it through the proper parameter mode, there is often no reason to copy it at all.

When a copy is needed, the language allows it. But the compiler makes sure the programmer notices.

The core rule remains simple:

Assignment copies values. It never creates aliases.

Zero Is Initialization (ZII)

I would like to thank Casey Muratori for giving a useful name to a practice I had already tried to follow wherever possible: Zero Is Initialization. Casey is also where I first encountered the term plex, in his talk The Big OOPS, though he credits the term to another source.

Precedent variables are available from the moment they are declared. They do not need to be explicitly instanced, and in most cases they do not need to be explicitly disposed. Their storage is owned by the declaring scope, and when that scope ends, the storage is released or finalized according to the rules of the type.

This raises an important question: how are declared variables initialized?

Precedent’s answer is simple:

All declared storage begins as zero.

When a variable is declared, Precedent ensures that its storage is initialized to zero bytes. Newly created collection elements are also zero-initialized. Variant payloads activated inside a plex are zero-initialized when required by the plex semantics.

Unknown initial state is not useful to structured code. If a variable contains arbitrary bytes, the programmer cannot safely reason about it. Many object-oriented languages address this through constructor mechanisms, but Precedent does not use ordinary constructors as its primary initialization model. Instead, it uses zero initialization as the baseline.

Historically, automatic zero initialization was often avoided because it could be viewed as an unnecessary performance cost. A compiler might need to emit code in a function prologue to clear local storage before use. Larger stack frames or large temporary buffers can make that cost visible.

However, the alternative is not free either. Constructor-heavy initialization can involve procedure calls, calling convention overhead, hidden control flow, and user-written initialization logic that must be maintained and called correctly.

Precedent’s position is that a predictable known initial state is worth the cost, and that the cost can often be minimized.

Modern systems already rely heavily on zeroed memory. For example, when an operating system provides newly allocated pages to a process, those pages must not contain data from another process. For security reasons, mainstream protected operating systems provide zeroed pages to user programs. This does not make all zeroing free, but it does mean that zero-filled memory is already a normal part of system behavior.

Local zeroing is also often cheap, especially compared to the complexity it avoids. In addition, the compiler can omit redundant zeroing in simple cases. If a variable is definitely assigned before any possible read, then zeroing that variable first may be unnecessary. Precedent may take these straightforward opportunities where they are safe and cheap to detect.

ZII means that every entity begins in a known state.

This does not mean every value is immediately semantically ready for every operation. A zeroed file abstraction is not an open file. A zeroed socket is not a connected socket. A zeroed parser may not yet have input. But the state is known, valid, and inert. Fields can indicate whether a resource has been opened, whether a buffer has been filled, or whether a structure has been semantically prepared.

The important rule is that zero is not garbage.

For Precedent types, the zero state should be a valid state. It may be empty, inactive, closed, unset, or unprepared, but it should be safe to finalize, safe to overwrite, and safe to reason about.

Finalization is a separate concern.

In the vast majority of cases, no explicit finalization logic is required. If a value owns only compiler-managed storage, that storage can be released when the owning scope ends. Collections can release their backing storage. Plain structs can simply disappear with their scope.

The exception is external resource ownership.

Files, sockets, windows, operating-system handles, graphics resources, mapped memory, locks, and similar resources may require explicit tear-down. For these cases, Precedent allows abstractions to define finalization behavior. When an abstraction variable leaves scope, the compiler emits the required finalization logic. A zeroed abstraction should represent an inert state, so finalization can safely determine whether there is actually anything to release.

Resource-owning abstractions may also be no-copy. This prevents accidental duplication of ownership, such as copying a file handle into two variables that both later attempt to close it.

Together, ZII and finalization give Precedent a simple resource model:

  • storage begins in a known zero state.
  • ordinary values require no explicit construction.
  • compiler-managed storage is released by scope.
  • resource-owning abstractions finalize by scope.
  • no-copy annotations prevent unsafe ownership duplication.

The result is a language where initialization and finalization are consistent with the broader design goal: the least ceremony needed to achieve predictable, intuitive behavior.

Interoperability

No modern systems language is useful unless it can interoperate with its target architecture and platform.

Most operating systems, firmware environments, system libraries, and platform APIs expose some form of C-compatible interface. Even when the implementation language is not C, the public ABI is often C-like: functions, structs, primitive values, pointers, handles, and calling conventions.

Precedent must therefore be able to call C APIs and be called from C-compatible environments.

This means that some of Precedent’s normal language guarantees necessarily stop at the foreign boundary. A C API does not know about Precedent’s lexical ownership model. It does not know about collection semantics, no-copy propagation, plex discriminators, abstraction finalization, or scoped storage rules. It sees memory addresses, primitive values, structs, and calling conventions.

The most important concern is pointer support.

A language cannot use C APIs seriously without supporting pointers to data. Pointers are the baseline mechanism by which C code passes buffers, structs, arrays, output parameters, optional values, and opaque handles.

For this reason, Precedent supports typed pointer data types.

However, typed pointers are explicitly unsafe in Precedent. Declaring or using them requires the programmer to state in source that they are stepping outside the ordinary ownership and lifetime model.

A pointer may refer to foreign memory.
A pointer may be null.
A pointer may outlive the thing it points to.
A pointer may alias mutable storage.
A pointer may point to memory whose lifetime is controlled by another library.
A pointer may represent an operating-system object, hardware buffer, mapped region, or ABI-specific structure.

None of this conforms to Precedent’s normal default semantics.

Precedent’s ordinary model is based on declared storage, scoped lifetime, copy assignment, parameter borrowing, compiler-known collections, and explicit unsafe escape hatches. Pointers exist because real systems programming requires them, not because they are the preferred way to model ordinary program structure.

Precedent’s standard runtime libraries wrap common external APIs behind abstractions where doing so preserves the language’s normal semantics. A file abstraction can wrap operating-system file calls. A socket abstraction can wrap networking calls. A window abstraction can wrap platform window handles and event loops. These abstractions can use unsafe pointers and foreign calls internally while presenting a safer, more Precedent-like interface to ordinary code.

This allows unsafe interop to be localized.

However, Precedent does not hide the foreign world completely. It remains possible to call operating-system APIs, dynamically loaded libraries, firmware interfaces, and C-compatible functions directly. Such calls may require explicit calling conventions, typed pointers, ABI-compatible structs, unions, primitive types, and platform-specific declarations.

This is intentional.

Precedent is meant to be an ergonomic systems development language, not a sandbox. It should make structured, safe, predictable code natural, but it must not prevent low-level work when low-level work is required.

The goal is to remove unnecessary sharp edges from ordinary programming, while still leaving the real tools available to the programmer.

Precedent is not a padded room. It is a workshop with the dangerous tools clearly marked.

The Compiler Is a One-Stop Shop

Precedent is intended to be an entire native toolchain in a single application.

More specifically, Precedent declines to participate in the traditional “chain” model of compiler tooling where source code is passed through a separate compiler, assembler, linker, build system, and debugger pipeline. That model made sense historically, and it remains useful in many ecosystems, but it is not the only possible approach.

Precedent is designed as an integrated compiler system.

It contains native instruction encoders for x86-64 and AArch64 rather than depending on an external assembler. It contains its own internal module linker rather than relying on a traditional object-file linker. It contains a JIT-style execution model with breakpoint support for in-memory incremental linking and direct execution. It understands its own source structure and module dependencies, allowing source files to be discovered through relative paths and source-level directory searches.

The goal is simple:

Source code should become a working executable through the compiler itself.

For ordinary Precedent projects, all that should be required is the compiler, the source tree, and the switches that describe the desired output form.

The compiler is able to:

  • Parse source units.
  • Discover source dependencies.
  • Lower source code to Precode.
  • Lower Precode to native machine code.
  • Link internal modules.
  • Emit executable binaries.
  • Hot-link incrementally in memory.
  • Execute code directly when requested.

Intermediate forms do not need to be written to disk as ordinary build artifacts. Source code is lowered to semantic structures, then to Precode structures, then to native code, then to an executable image, all in memory.

This makes the compilation model simpler and more direct.

A traditional build chain often passes partial results through disk: generated assembly files, object files, dependency files, libraries, and executable outputs. Precedent avoids this where possible. It does not need to reload partial binary blobs from disk during ordinary incremental work because the compiler can retain and manage the relevant state in memory.

Incremental linking does not suffer from stale intermediate files because the compiler owns the incremental state. If a source unit changes, the compiler can invalidate the affected semantic and code-generation products directly, relink the affected internal modules, and update the executable image or hot-linked runtime state.

This also gives Precedent opportunities for speed.

Compilation is designed to be fast. The compiler can use in-memory state caching, parallel parsing, parallel lowering, parallel code generation, and incremental relinking where appropriate. It does not need to coordinate a large collection of external tools for ordinary builds.

This does not mean external tooling has no place. Packaging systems, asset pipelines, continuous integration, documentation generators, test runners, installers, and mixed-language projects may still need orchestration. But Precedent’s core source-to-native-executable path does not require a separate assembler, linker, or make-style build system.

The compiler is the build system for Precedent code.

This design fits the broader philosophy of the language. Precedent favors explicit structure and predictable lowering. Its toolchain follows the same idea: the path from source to executable is visible, integrated, and controlled by one system rather than scattered across a chain of loosely connected tools.

Predictable Lowering Over Heroic Optimization

Precedent has great respect for the optimization efforts made by modern compilers.

There was once a common understanding that no compiler could outperform a skilled human writing assembly by hand. For a long time, that was often true. Translating from a higher-level language to a lower-level machine representation usually required compromise.

Today, that assumption is less certain. Modern optimizing compilers can generate excellent machine code. They know large instruction sets, track target-specific behavior, perform deep analysis, and apply transformations no human would want to perform manually across a large codebase.

However, optimizing compilers have one trait Precedent intentionally avoids as a central design goal: they rely heavily on heuristic transformation.

This is not the same as random guessing. Modern compiler optimization is sophisticated and deeply engineered. But it is still often based on analysis, prediction, target models, and transformations whose results are not always obvious from the source code. The generated code may be fast, but the path from source to machine behavior can become difficult for the programmer to reason about.

Precedent takes a different position.

The compiler should not be expected to rescue poor structure. The language should instead give the programmer tools to express good structure directly.

Many existing optimizing compilers operate under constraints imposed by older languages. A compiler targeting C or C++ does not get to redesign C or C++ syntax and semantics to expose better data layout, scoped ownership, collection behavior, or parallel work structure. It must accept the source model it is given, then do its best to generate efficient code from that model.

Precedent is a new language, so it can make different choices at the syntax and semantic level.

Much of Precedent’s design is focused on data-structure clarity, cache-friendly storage, and predictable execution. It is procedural in part to avoid unnecessary pointer chasing and indirection. It provides arrays, lists, rings, trees, graphs, dictionaries, plexes, abstractions, handles, and SIMD-aware mathematical types so the programmer can express the intended structure of the program directly.

This is a different optimization philosophy.

Many languages allow the programmer to describe the “essence” of an operation while the compiler performs heroic work to transform that description into efficient machine code. Precedent instead asks the programmer to choose data shapes, access patterns, ownership boundaries, and parallel work boundaries explicitly.

The compiler does not intentionally leave obvious performance opportunities on the floor. It may perform local and deterministic optimizations where they are safe and clear: choosing compact instruction encodings, folding constants, eliminating redundant zeroing, simplifying branches, inlining compiler-synthesized collection fast paths, or lowering known vector operations to appropriate target instructions.

But Precedent favors predictable lowering over broad heuristic optimization.

This matters in real-time and performance-sensitive programs. Consider a video game engine loop operating at 60 or 120 frames per second. In a tight simulation, animation, audio, or rendering-support workload, consistent timing may matter as much as absolute speed. If work is divided across multiple cores, the frame cannot proceed until the required work is complete. An optimization that improves one portion of the workload but leaves the overall synchronization point unchanged may not improve the frame. Worse, opaque transformations can make timing harder to predict and performance harder to reason about.

Precedent therefore treats performance as something the programmer expresses through structure.

If data should be contiguous, use an array.
If data should be processed in parallel slices, use a slicer.
If work should be scheduled item-by-item, use a worker system.
If identity should survive insertion and removal, use handles.
If alternatives should share common storage without inheritance, use a plex.
If mathematical operations should map to SIMD-friendly forms, use the appropriate vector, matrix, quaternion, or tensor types.

The compiler’s job is to lower those choices predictably.

This does not mean Precedent is naive. It means the primary optimization window is opened by the programmer through language semantics, not discovered after the fact by heroic analysis. The programmer determines how the program approaches the hardware. The compiler preserves and lowers that intent.

Precedent’s goal is not to make bad code magically fast.Its goal is to make good structure natural, visible, and predictably executable.

Core Principles

The central idea of Precedent can be summarized simply as:

The shape of the program’s data should be visible in the source, owned by lexical scope, and lowered predictably by the compiler.
Abstractions exist to protect invariants, not to create inheritance hierarchies.
Plex types exist to represent alternatives, not to simulate subclasses.
Collections exist to express storage and topology, not to hide arbitrary allocation.
Unsafe pointers exist for the cases where normal structure is not enough, not as the default modeling tool.
Optimization exists as a consequence of good structure, not as a rescue operation after structure has been lost.
Precedent’s goal is to make the good shape of a program easier to write than the bad one.

What Precedent Refuses

Precedent is intentionally not a full-featured productivity language in the usual commercial sense. It does not aim to accumulate every feature that programmers might request, every syntax shortcut that saves a few keystrokes, or every abstraction that can be marketed as modern convenience.

The compiler provides primitives. A minimal practical runtime is layered on top of those primitives. Libraries may then be built on top of the runtime to provide higher-level functionality, but those libraries are distinct from the compiler itself. The compiler’s job is to model and lower a program’s intent, not to become the entire software ecosystem.

Features such as broad application frameworks, database abstraction layers, built-in document parsers, entity systems, and domain-specific convenience APIs do not belong in the compiler merely because some programs may eventually need them. If a feature can be written cleanly as a library, it should not become a language feature. If a feature exists mainly to imitate another ecosystem, Precedent does not need it.

Precedent also rejects endless syntax growth. A small fixed language is easier to understand, easier to test, easier to document, and easier to lower predictably. The goal is not to make every programming style comfortable. The goal is to make the intended style clear, explicit, and reliable.

Absence, failure, optional results, and foreign invalid states still exist in real programs, but they should be modeled deliberately. Precedent does not treat ambient nullability as the default state of ordinary program structure. When a program needs to represent absence, it should do so explicitly through the appropriate data structure, result value, handle state, abstraction state, or unsafe interop boundary.

Precedent is a small compiler for explicit programs. It favors fixed primitives, readable control flow, simple runtime calls, and bounded implementation over feature accumulation. Features are not added because they are fashionable, familiar, or requested. Features are admitted only when they are necessary to express the intended class of programs cleanly and cannot be better implemented in libraries.

Precedent’s restraint is not a temporary limitation.
It is part of the design.