Layout Options

Which layout option do you want to use?

Color Schemes

Which theme color do you want to use? Select from here.

Programming A Governed Model of Deterministic Program Execution

Joined
Jul 20, 2025
Messages
145
Governed Model of Program Execution
Most introductory programming courses teach execution like: a program runs from top to bottom, performing calculations and occasionally interacting with the outside world—printing text, writing files, sending network requests. In many languages, calculation and action are interwoven. I propose a different structure. I will seperate calculation from action and insert a governing runtime between them. The result is a deterministic, auditable model of execution implemented in Haskell. To understand the design, it is helpful to clarify two importnat ideas:


A pure function is a function that only computes a result from its inputs. It does not modify files, read from the network, print to the screen, or depend on hidden global state. Given the same inputs, it always returns the same output. In contrast, an effect (short for “side effect”) is an action that changes or observes the external world such as writing a file, sleeping for a second, or printing a line.


Most programs mix these freely. I won't. Effects will be treated as descriptions of actions of possible actions, and actually performing them will be postponed until a later, controlled stage.




Separating Calculation from Action
My language evaluates expressions into values. Crucially, evaluating an effect does not perform it. It produces a value that describes it.


In the evaluator:


Haskell:
  evalExpr _ (Emit ed) = Right (VEffect ed)

When the program encounters something like “emit this effect,” the interpreter returns a VEffect value. It does not run the effect and the interpretator remains purely computational.
After evaluation finishes, the system collects all the effect descriptions that were produced:


Haskell:
  collectEffects :: [Value] -> [EffectDescription]
  collectEffects [] = []
  collectEffects (VEffect ed : vs) = ed : collectEffects vs
  collectEffects (VList subVs : vs) = collectEffects subVs ++ collectEffects vs
  collectEffects (_ : vs) = collectEffects vs
This function walks through the final values of the program and extracts the list of EffectDescription structures. Everything else is ignored. At this point, the program has not changed the world. It has only described the changes it would like to make.




Why Treat Effects as Data?
Treating effects as data provides several advantages:


  1. Predictability: The interpreter is entirely deterministic. It only computes values.
  2. Inspectability: Before anything runs, the full set of requested actions is visible.
  3. Control: Another component can decide which actions should actually occur.
The system assigns each effect a unique identifier derived from its content:


Haskell:
  EffectID = SHA256(canonicalEffectBytes)
An effect's identity depends on what it is. If two effect descriptions are structurally identical, they have the same ID. This makes effects content-addressed. Identity is not based on when they are created or where they appear in the program. It is based solely on structure.




Dependency Graphs and Deterministic Scheduling
Effects may depend on other effects. For example, “write to file B” might depend on “create directory A.” These dependencies are explicitly recorded. The runtime constructs a dependency graph and executes effects in layers. Effects with no unmet dependencies form a “wave” and may run in parallel:


Haskell:
  currentEntries <- mapConcurrently realizeTransition ready
  return (sortOn heEffectID currentEntries ++ nextEntries)
All effects in a layer are executed concurrently. After execution, the results are sorted by `EffectID' before being recorded. This ensures that, even though execution is parallel, the historical record is deterministic.
If some effects cannot run because their dependencies failed, they are rejected. Nothing is silently retried or implicitly reordered.




Authorization Before Execution
Before executing an effect, the runtime checks its historical record. If an identical effect (same `EffectID') has already succeeded, it is not executed again.


Haskell:
  governorAdmit :: History -> EffectDescription -> Decision
If the effect has already been completed successfully, the decision is “reject.” Otherwise, it may proceed.
This guarantees idempotence. The key architectural point here is that the component deciding whether an action may occur is different from the component that knows how to perform it.




Executing and Verifying Effects
Each type of effect has an associated specification:


Haskell:
  data EffectSpec = EffectSpec
  { esRun    :: EffectDescription -> IO Outcome
    ,esVerify :: EffectDescription -> IO Outcome
  }

The runtime:


  • Decides whether the effect is allowed.
  • Runs it if permitted.
  • Verifies the result.
  • Records the outcome in a ledger.



The Ledger
Every attempted effect is appended to a persistent ledger stored in SQLite.
Each entry records:


  • The time of execution
  • The effect’s unique ID
  • Its dependencies
  • Whether it was admitted or rejected
  • Whether it succeeded or failed
The ledger is append-only. Past entries are never modified. This creates a durable audit trail of system behavior. The system also checks for “symmetry”: identical effect IDs should not receive inconsistent admission decisions across runs. If they do, the system can detect the inconsistency.




Language-Level Constraints
The language further enforces discipline by restricting what effect definitions may contain. Effect bodies must be simple, they cannot contain branching (if), local bindings (let), or nested functions.
For example:


Haskell:
  isControlFlow (Seq _)       = True
  isControlFlow (Let _ _ _)   = True
  isControlFlow (Fun _ _ _ _) = True
  isControlFlow (If _ _ _)    = True

If an effect body includes any of these constructs, elaboration fails.This restriction ensures that effects are declarative descriptions, not mini-programs with hidden logic.
Decision-making remains in the pure computation layer or in the governing runtime.




Conclusion
This architecture reframes program execution. Instead of:


Compute and act simultaneously
My language enforces: :


  • Compute desired actions (purely).
  • Identify and organize them.
  • Decide which are admissible.
  • Execute them in a controlled order.
  • Verify and record results immutably.

The separation increases transparency and determinism. It becomes possible to inspect all intended changes before they occur. Execution history is stable and replayable. Duplicate actions are automatically suppressed.


In contrast to conventional interpreters, where IO can occur at any point in evaluation, this design introduces structure and governance.
 
Joined
Nov 29, 2025
Messages
359
Are you familiar with the term computational provenance? Your treatise reminded me of it. What you've described is a fairly comprehensive (and perhaps novel, would require some research) approach to execution provenance. Most systems with provenance typically focus more on data provenance (tracking the origins and history of data, how it was transformed, etc.).

I suppose there is some opportunity to combine the two into a complete system that covers both effect and data provenance. There's also a couple of companies I know of (*cough* Palantir) that are building LLM focused provenance and governance systems, and are more focused on LLM orchestration rather than on a generalized strict deterministic programming model.

What are you planning on using this for, if anything?
 
Lieutenant
Joined
Jan 30, 2026
Messages
856
Governed Model of Program Execution
Most introductory programming courses teach execution like: a program runs from top to bottom, performing calculations and occasionally interacting with the outside world—printing text, writing files, sending network requests. In many languages, calculation and action are interwoven. I propose a different structure. I will seperate calculation from action and insert a governing runtime between them. The result is a deterministic, auditable model of execution implemented in Haskell. To understand the design, it is helpful to clarify two importnat ideas:


A pure function is a function that only computes a result from its inputs. It does not modify files, read from the network, print to the screen, or depend on hidden global state. Given the same inputs, it always returns the same output. In contrast, an effect (short for “side effect”) is an action that changes or observes the external world such as writing a file, sleeping for a second, or printing a line.


Most programs mix these freely. I won't. Effects will be treated as descriptions of actions of possible actions, and actually performing them will be postponed until a later, controlled stage.




Separating Calculation from Action
My language evaluates expressions into values. Crucially, evaluating an effect does not perform it. It produces a value that describes it.


In the evaluator:


Haskell:
  evalExpr _ (Emit ed) = Right (VEffect ed)

When the program encounters something like “emit this effect,” the interpreter returns a VEffect value. It does not run the effect and the interpretator remains purely computational.
After evaluation finishes, the system collects all the effect descriptions that were produced:


Haskell:
  collectEffects :: [Value] -> [EffectDescription]
  collectEffects [] = []
  collectEffects (VEffect ed : vs) = ed : collectEffects vs
  collectEffects (VList subVs : vs) = collectEffects subVs ++ collectEffects vs
  collectEffects (_ : vs) = collectEffects vs
This function walks through the final values of the program and extracts the list of EffectDescription structures. Everything else is ignored. At this point, the program has not changed the world. It has only described the changes it would like to make.




Why Treat Effects as Data?
Treating effects as data provides several advantages:



  1. Predictability: The interpreter is entirely deterministic. It only computes values.

  2. Inspectability: Before anything runs, the full set of requested actions is visible.

  3. Control: Another component can decide which actions should actually occur.

The system assigns each effect a unique identifier derived from its content:


Haskell:
  EffectID = SHA256(canonicalEffectBytes)
An effect's identity depends on what it is. If two effect descriptions are structurally identical, they have the same ID. This makes effects content-addressed. Identity is not based on when they are created or where they appear in the program. It is based solely on structure.




Dependency Graphs and Deterministic Scheduling
Effects may depend on other effects. For example, “write to file B” might depend on “create directory A.” These dependencies are explicitly recorded. The runtime constructs a dependency graph and executes effects in layers. Effects with no unmet dependencies form a “wave” and may run in parallel:


Haskell:
  currentEntries <- mapConcurrently realizeTransition ready
  return (sortOn heEffectID currentEntries ++ nextEntries)
All effects in a layer are executed concurrently. After execution, the results are sorted by `EffectID' before being recorded. This ensures that, even though execution is parallel, the historical record is deterministic.
If some effects cannot run because their dependencies failed, they are rejected. Nothing is silently retried or implicitly reordered.




Authorization Before Execution
Before executing an effect, the runtime checks its historical record. If an identical effect (same `EffectID') has already succeeded, it is not executed again.


Haskell:
  governorAdmit :: History -> EffectDescription -> Decision
If the effect has already been completed successfully, the decision is “reject.” Otherwise, it may proceed.
This guarantees idempotence. The key architectural point here is that the component deciding whether an action may occur is different from the component that knows how to perform it.




Executing and Verifying Effects
Each type of effect has an associated specification:


Haskell:
  data EffectSpec = EffectSpec
  { esRun    :: EffectDescription -> IO Outcome
    ,esVerify :: EffectDescription -> IO Outcome
  }

The runtime:



  • Decides whether the effect is allowed.

  • Runs it if permitted.

  • Verifies the result.

  • Records the outcome in a ledger.




The Ledger
Every attempted effect is appended to a persistent ledger stored in SQLite.
Each entry records:



  • The time of execution

  • The effect’s unique ID

  • Its dependencies

  • Whether it was admitted or rejected

  • Whether it succeeded or failed

The ledger is append-only. Past entries are never modified. This creates a durable audit trail of system behavior. The system also checks for “symmetry”: identical effect IDs should not receive inconsistent admission decisions across runs. If they do, the system can detect the inconsistency.




Language-Level Constraints
The language further enforces discipline by restricting what effect definitions may contain. Effect bodies must be simple, they cannot contain branching (if), local bindings (let), or nested functions.
For example:


Haskell:
  isControlFlow (Seq _)       = True
  isControlFlow (Let _ _ _)   = True
  isControlFlow (Fun _ _ _) = True
  isControlFlow (If _ _ _)    = True

If an effect body includes any of these constructs, elaboration fails.This restriction ensures that effects are declarative descriptions, not mini-programs with hidden logic.
Decision-making remains in the pure computation layer or in the governing runtime.




Conclusion
This architecture reframes program execution. Instead of:



My language enforces: :



  • Compute desired actions (purely).

  • Identify and organize them.

  • Decide which are admissible.

  • Execute them in a controlled order.

  • Verify and record results immutably.


The separation increases transparency and determinism. It becomes possible to inspect all intended changes before they occur. Execution history is stable and replayable. Duplicate actions are automatically suppressed.


In contrast to conventional interpreters, where IO can occur at any point in evaluation, this design introduces structure and governance.
WOW amazing write up! You mentioned before that your getting your CS degree?
 
Joined
Jul 20, 2025
Messages
145
So are you planning on implementing this into finance or healthcare? Something with the blockchain?
No. Finance and healthcare have many complex regulations. I feel that would make me spend more energy on compliance adapters instead of deepening the core model. Blockhains(I think) solve distributed trust without central authority, but i already have a single authority, a single ledger, and deterministic execution. This is not a problem i have.
. Any plans to handle AI agentic workflows in the build pipeline?
This idea seems very interesting. We wouldd have something like: model output -> EffectDescription -> governor -> execution -> ledger. Then, hallucinated tool loops would be rejected, runs would be replayable, and we could audit side effects. This could also allow for greater policy enforcement
 
Joined
Feb 10, 2026
Messages
90
Governed Model of Program Execution
Most introductory programming courses teach execution like: a program runs from top to bottom, performing calculations and occasionally interacting with the outside world—printing text, writing files, sending network requests. In many languages, calculation and action are interwoven. I propose a different structure. I will seperate calculation from action and insert a governing runtime between them. The result is a deterministic, auditable model of execution implemented in Haskell. To understand the design, it is helpful to clarify two importnat ideas:


A pure function is a function that only computes a result from its inputs. It does not modify files, read from the network, print to the screen, or depend on hidden global state. Given the same inputs, it always returns the same output. In contrast, an effect (short for “side effect”) is an action that changes or observes the external world such as writing a file, sleeping for a second, or printing a line.


Most programs mix these freely. I won't. Effects will be treated as descriptions of actions of possible actions, and actually performing them will be postponed until a later, controlled stage.




Separating Calculation from Action
My language evaluates expressions into values. Crucially, evaluating an effect does not perform it. It produces a value that describes it.


In the evaluator:


Haskell:
  evalExpr _ (Emit ed) = Right (VEffect ed)

When the program encounters something like “emit this effect,” the interpreter returns a VEffect value. It does not run the effect and the interpretator remains purely computational.
After evaluation finishes, the system collects all the effect descriptions that were produced:


Haskell:
  collectEffects :: [Value] -> [EffectDescription]
  collectEffects [] = []
  collectEffects (VEffect ed : vs) = ed : collectEffects vs
  collectEffects (VList subVs : vs) = collectEffects subVs ++ collectEffects vs
  collectEffects (_ : vs) = collectEffects vs
This function walks through the final values of the program and extracts the list of EffectDescription structures. Everything else is ignored. At this point, the program has not changed the world. It has only described the changes it would like to make.




Why Treat Effects as Data?
Treating effects as data provides several advantages:



  1. Predictability: The interpreter is entirely deterministic. It only computes values.

  2. Inspectability: Before anything runs, the full set of requested actions is visible.

  3. Control: Another component can decide which actions should actually occur.

The system assigns each effect a unique identifier derived from its content:


Haskell:
  EffectID = SHA256(canonicalEffectBytes)
An effect's identity depends on what it is. If two effect descriptions are structurally identical, they have the same ID. This makes effects content-addressed. Identity is not based on when they are created or where they appear in the program. It is based solely on structure.




Dependency Graphs and Deterministic Scheduling
Effects may depend on other effects. For example, “write to file B” might depend on “create directory A.” These dependencies are explicitly recorded. The runtime constructs a dependency graph and executes effects in layers. Effects with no unmet dependencies form a “wave” and may run in parallel:


Haskell:
  currentEntries <- mapConcurrently realizeTransition ready
  return (sortOn heEffectID currentEntries ++ nextEntries)
All effects in a layer are executed concurrently. After execution, the results are sorted by `EffectID' before being recorded. This ensures that, even though execution is parallel, the historical record is deterministic.
If some effects cannot run because their dependencies failed, they are rejected. Nothing is silently retried or implicitly reordered.




Authorization Before Execution
Before executing an effect, the runtime checks its historical record. If an identical effect (same `EffectID') has already succeeded, it is not executed again.


Haskell:
  governorAdmit :: History -> EffectDescription -> Decision
If the effect has already been completed successfully, the decision is “reject.” Otherwise, it may proceed.
This guarantees idempotence. The key architectural point here is that the component deciding whether an action may occur is different from the component that knows how to perform it.




Executing and Verifying Effects
Each type of effect has an associated specification:


Haskell:
  data EffectSpec = EffectSpec
  { esRun    :: EffectDescription -> IO Outcome
    ,esVerify :: EffectDescription -> IO Outcome
  }

The runtime:



  • Decides whether the effect is allowed.

  • Runs it if permitted.

  • Verifies the result.

  • Records the outcome in a ledger.




The Ledger
Every attempted effect is appended to a persistent ledger stored in SQLite.
Each entry records:



  • The time of execution

  • The effect’s unique ID

  • Its dependencies

  • Whether it was admitted or rejected

  • Whether it succeeded or failed

The ledger is append-only. Past entries are never modified. This creates a durable audit trail of system behavior. The system also checks for “symmetry”: identical effect IDs should not receive inconsistent admission decisions across runs. If they do, the system can detect the inconsistency.




Language-Level Constraints
The language further enforces discipline by restricting what effect definitions may contain. Effect bodies must be simple, they cannot contain branching (if), local bindings (let), or nested functions.
For example:


Haskell:
  isControlFlow (Seq _)       = True
  isControlFlow (Let _ _ _)   = True
  isControlFlow (Fun _ _ _ _) = True
  isControlFlow (If _ _ _)    = True

If an effect body includes any of these constructs, elaboration fails.This restriction ensures that effects are declarative descriptions, not mini-programs with hidden logic.
Decision-making remains in the pure computation layer or in the governing runtime.




Conclusion
This architecture reframes program execution. Instead of:



My language enforces: :



  • Compute desired actions (purely).

  • Identify and organize them.

  • Decide which are admissible.

  • Execute them in a controlled order.

  • Verify and record results immutably.


The separation increases transparency and determinism. It becomes possible to inspect all intended changes before they occur. Execution history is stable and replayable. Duplicate actions are automatically suppressed.


In contrast to conventional interpreters, where IO can occur at any point in evaluation, this design introduces structure and governance.
WOah! This is honestly so cool.

I am a beginner in CS so excuse my questions if they are stupid but:
Does that mean that the functions are not allowed to rely on the results of effects? Because otherwise, I can imagine how a function would not be successfully computed if its dependent on an effect.

Also, this seems like a costly system in terms of memory, no? :o
I mean, effects seem to accumulate and only execute at the end, as well as the fact that dependency graphs are produced (and authoritization checks and auditing). I mean, you did mention "waves" being run in parallel as well as merging identical effects I think? I can see how that helps though I imagine it would still not be memory-efficient as other programming languages, right? Would it be safe to assume this is an intentional trade-off between memory and determinism and auditablity? :o


Very insightful though. Are you mainly interested in programming language research? Also, what do you think of OCaml?


Sorry for the many questions but this is a very thought-provoking post XDD
 
Joined
Jul 20, 2025
Messages
145
Does that mean that the functions are not allowed to rely on the results of effects? Because otherwise, I can imagine how a function would not be successfully computed if its dependent on an effect.
Not in the same execution pass.
Also, this seems like a costly system in terms of memory, no? :o
Yes. During evaluation I store: AST, Values, Lists of `Effect`, Hashes, and our dependency graph, and during execution we have our ledger entries in sqlite and our dependency resolution state. This is heavy, but not absurdly heavy because 1000 identical `(print "hello")` will all hash to the same ID. The memory growth depends on structural uniqueness.
I mean, effects seem to accumulate and only execute at the end
During evaluation: The program produces values. Some of those values are Effect descriptions. At the end, the runtime walks the result graph and extracts them.
Only the final value graph, the set of reachable effects, and dependency relationships, and ledger entries would be stored.
Would it be safe to assume this is an intentional trade-off between memory and determinism and auditablity? :o
Yes.
 

qqq

Member
Joined
Feb 4, 2026
Messages
145
Leave zero days if you love Allah

Light Yagami Kira GIF
 
Joined
Feb 10, 2026
Messages
90
Not in the same execution pass.

Yes. During evaluation I store: AST, Values, Lists of `Effect`, Hashes, and our dependency graph, and during execution we have our ledger entries in sqlite and our dependency resolution state. This is heavy, but not absurdly heavy because 1000 identical `(print "hello")` will all hash to the same ID. The memory growth depends on structural uniqueness.

During evaluation: The program produces values. Some of those values are Effect descriptions. At the end, the runtime walks the result graph and extracts them.
Only the final value graph, the set of reachable effects, and dependency relationships, and ledger entries would be stored.

Yes.
Very interesting! Thanks for indulging with my questions :>

Unrelated but your treatment of effects (specifically with hashing and how identical effects can collapse into a single "node") reminds me of the RLE algorithm. Terrifyingly efficient with very repetitive work but not quite so with diverse and unique work.

Good post! I wish following you meant I get notified of future posts but alas, that does not seem to be the case :/
 
Activity
So far there's no one here
Top