Dependent Types and Real-World Systems

Post 5 · Phase 2 — Logic

So far, we have built up a small but expressive foundation:

This already allows us to model systems in a structured way.

But an important question remains: How do we ensure that certain properties always hold?

For example: an asset should never have more than one owner, a transfer should not create invalid states, and permissions should remain consistent.

We can state these informally. But we want more than that. We want these properties to be part of the system itself.

From Constraints to Guarantees

In earlier posts, we introduced constraints: "An asset has at most one owner."

These constraints define what valid states look like.

Now we take the next step: expressing properties that must hold across all possible states and transitions.

This moves us from describing valid configurations to reasoning about entire systems.

A Glimpse of Dependent Types

Systems like Lean and Idris show how types can be used to express precise properties.

In these systems, types are not just categories like integers or strings. They can express relationships and constraints.

For example, a type might encode "a list of length n" or "a value greater than zero".

This is the essence of dependent types: types that depend on values, and can express detailed properties about them.

Bringing This Into Systems

Spine draws inspiration from these ideas, but applies them in a different setting.

Instead of focusing primarily on mathematical objects or isolated functions, we are dealing with evolving systems, facts and relationships, and actions over time.

The question becomes: How can we express properties of an entire system in a precise way?

Invariants in the Asset System

Let's return to our example.

We already have the idea that an asset has at most one owner. In Spine, this is not just a guideline. It is part of the system's fact model — a constraint defined alongside the facts themselves, not layered on after the code is written.

It applies to all states, across all transitions, regardless of which effects are used.

This kind of property is often called an invariant.

Rules Must Preserve Invariants

Once invariants are part of the system, they impose obligations.

For example: the transfer rule must not create multiple owners. No sequence of effects should violate ownership uniqueness.

This leads to an important shift: it is not enough for a rule to describe a change — it must preserve the correctness of the system.

From Local Checks to Global Reasoning

In many programs, correctness is enforced through tests, runtime checks, and careful discipline.

These approaches are useful, but they are partial and external to the program's structure.

In a system with richer types and constraints, we can instead ask:

These are not just runtime questions — they are questions about the system itself.

There is a further subtlety: some meaningful constraints can be stated but not evaluated until after a violation has occurred. For example, "the transfer of ownership must be notified to the authority within two weeks" cannot be checked until the deadline has passed. These temporal constraints require different treatment from the static invariants above.

Expressing Properties of Behavior

We can now start to express properties like:

These are not tied to a single function or operation. They describe the behavior of the system as a whole.

Some of these properties go beyond logical correctness. For instance, we may want to express that resources are never duplicated or lost — that every transfer accounts for what is consumed and what is produced. This kind of guarantee involves not just what is true, but how many times each fact is used.

Not Just Mathematics

At this point, it might seem like we are moving toward a theorem prover.

Systems like Lean are extremely powerful in expressing and proving properties.

Spine shares some of this foundation, but has a different focus. It is not primarily about proving abstract theorems. Instead, it is about modeling real systems, expressing their constraints, and ensuring those constraints are respected.

Bridging Description and Guarantee

What makes this interesting is the combination:

These are not separate layers. They are all part of the same system.

A Subtle Shift

This leads to another shift in perspective: from writing programs that try to be correct, to describing systems where correctness is part of their structure.

Instead of asking "Did we handle all edge cases?", we can ask "Is it even possible to construct an invalid state?"

Looking Ahead

We now have structure, behavior, constraints, and guarantees.

In the next post, we will bring these elements together: how facts, rules, effects, and invariants interact, how they form a coherent whole, and how this differs from traditional approaches.

At that point, the shape of Spine as a unified system will start to become clear.

← Previous: Effects as System Semantics Next: Facts, Logic, and Effects in One Language →