Nick Hoff

A Language Designed to Be Written by Models

December 17, 2025

A language with authenticate, charge_card, tensor, sprite, interrupt, and pivot_table in its core would be worse than the languages it replaces. It would encode one committee's snapshot of software into grammar.

The solution is not to predict every domain programmers will touch. It is to standardize how a domain becomes legible to the compiler and runtime.

Part 1 argued that human-oriented source languages are not obviously the right canonical output of a probabilistic programmer. That claim is cheap until there is an alternative concrete enough to criticize. The alternative proposed here has three layers: a small semantic core, typed domain extensions, and adapters to existing ecosystems.

Universal core, domain extensions, and concrete adaptersA universal semantic core defines mechanisms such as types, effects, resources, and observations. Domain extensions define identity, payments, machine learning, spreadsheets, systems, and graphics. Concrete adapters connect those domains to providers and runtimes.UNIVERSAL COREtypescontractseffectscapabilitiesresourcesstatefailuretimeobservationsprovenanceDOMAIN EXTENSIONSidentitypaymentsrelational dataML trainingspreadsheetssystemsgraphicsCONCRETE ADAPTERSClerkStripePostgresPyTorchCUDAOpenXMLVulkanOS APIscompiler and runtimeMechanisms are standardized once; domain nouns and provider details remain replaceable.
The core standardizes semantic mechanisms. Domain packages define nouns and operations. Adapters connect them to concrete ecosystems.

The core is not a universal ontology. It knows about types, contracts, effects, capabilities, resources, state, failure, time, observation, and provenance. It does not know what Stripe, CUDA, Excel, or Vulkan is.

It has to contain actual code

The weak version of this proposal looks like deployment configuration:

service checkout {
  runtime = node
  database = postgres
}

That may be useful, but it is not a programming language. It cannot express how an order total is calculated, what happens when a customer is missing, how a subscription changes state, which operation may be retried, or what the user should see while an external event is delayed.

A generation language must express computation: records and variants, branching, data transformation, functions, state transitions, concurrency where it matters, typed failure, external effects, and user-visible outcomes. It also needs contracts strong enough for a compiler to reject a program that is locally well-formed but systemically incomplete.

Here is the Storefront operation from Part 1 with a little more context. The syntax remains provisional.

system Storefront {
  resource commerce: Database<CommerceSchema>
  capability customers: Authentication<User>
  capability billing: PaymentProcessor

  operation begin_checkout(
    request: BeginCheckout
  ) -> CheckoutRedirect
    requires [
      customers.authenticated(request.actor),
      request.cart.non_empty
    ]
    effects [
      commerce.write(PendingOrder),
      billing.create_checkout
    ]
    idempotent_by request.id
    ensures result.destination.is_https
  {
    customer = commerce.Customer.require_identity(
      request.actor.identity
    )

    checkout = billing.checkout.create {
      purchaser = customer
      product = AnnualPlan
      success_url = web.route("/account?checkout=success")
      cancel_url = web.route("/pricing")
    }

    commerce.PendingOrder.insert {
      user = request.actor.id
      checkout = checkout.id
    }

    return checkout.redirect
  }
}

This is application code. It performs a lookup, constructs a request, invokes an external capability, writes state, and returns a result. The declarations around the body are not comments. They constrain which callers are legal, what authority is needed, whether a retry is valid, and what the result must satisfy.

The strongest objection: this becomes a platform for everything

A "small core plus extensions" sounds responsible because every ambitious language proposal eventually says it. It can also be a way of hiding the ambition.

If extensions can define types, effects, lifecycle, simulators, lowerings, observations, verifiers, editors, and package metadata, we have recreated a compiler framework, package manager, build system, infrastructure planner, runtime protocol, and ontology project. If the core is too small, extensions cannot agree on anything useful. If it is too rich, the core becomes the universal language of software by another name.

That objection should constrain the prototype, not merely appear in a limitations section.

The first implementation should define only the extension contract required by one vertical slice. Unknown libraries enter through foreign interfaces. A concept earns semantic elevation only when doing so enables checking, authority control, planning, simulation, observation, verification, cross-backend lowering, or safer repair. Portability is a gradient: some operations may have several verified adapters, some one partial adapter, and some only an opaque escape hatch.

The goal is not to model everything. It is to stop pretending we understand things we do not.

What belongs in the core

The core should contain mechanisms that recur even when the domain nouns change:

This list is still too large for a first parser. It is an architectural boundary, not a sprint backlog.

Effects deserve special attention because they turn hidden behavior into an interface. A capability answers who has authority? An effect answers what externally visible class of action may happen? A precondition answers when is it legal? A postcondition answers what must be true afterward?

Languages such as Koka demonstrate the narrower proposition that effects can appear in function types and support static reasoning. This proposal uses that idea more coarsely. billing.create_checkout is not necessarily an algebraic effect handler. It is a semantic fact the planner, sandbox, auditor, and retry checker can all consume.

Canonical form matters here. The language should not offer five error models, three module systems, two resource-ownership schemes, and arbitrary serialization choices. Canonical does not mean terse. It means equivalent programs tend to converge on the same structure, so generation and repair do not spend probability mass on irrelevant variation.

The compiler should see obligations, not just statements

Semantic graph for the checkout operationAn HTTP checkout endpoint invokes begin checkout. Authentication, idempotency, effects, and an HTTPS postcondition connect to customer lookup, payment checkout creation, and a pending-order database write.POST /checkoutbegin_checkoutCustomer lookupCreate checkoutWrite PendingOrderAuthenticated actorIdempotency: request.idHTTPS redirectpreconditionretry identityeffect edgedata or control edgecontract edge
The compiler sees more than statements. It sees the operation's authority, effects, retry identity, and required outcome.

The graph behind begin_checkout connects several kinds of fact:

This representation does not prove that the payment provider is correct or that the chosen identity is globally unique. It gives deterministic tooling something better than pattern matching over arbitrary application code.

Resources also need stable identity. A database row, cloud bucket, model checkpoint, device buffer, and payment subscription have different lifecycles, but all can be created, observed, updated, transferred, or destroyed. If generated code changes filenames and line numbers on every repair, semantic resource identities give traces and diagnostics a stable address.

The model must be allowed to say "unknown"

Probabilistic programmers are good at filling blanks. A language should not force every blank to be filled.

authentication.provider = unresolved(
  "Choose a provider compatible with the customer identity policy"
)

Or, if the allowed set is known:

authentication.provider in [Clerk, Auth0, InternalOIDC]

An unresolved value is neither null nor a confident guess. It is a typed planning blocker. The compiler can propagate it, determine which operations depend on it, and ask a focused question when the answer becomes necessary. A deployment planner can refuse production while still allowing a local simulator.

This is one place where a model-facing language can be more honest than conventional source code. A placeholder string that happens to type-check is often worse than a compile error.

Domain extensions define semantics, not SDK wrappers

Payments do not belong in the universal core. A payment extension can still tell the system why a provider call is special:

domain commerce.payments {
  interface PaymentProcessor {
    resource Checkout {
      identity: CheckoutId
      status: CheckoutStatus
    }

    effect CreateCheckout {
      class = external_financial_mutation
      retry = idempotency_required
      audit = required
    }

    operation checkout.create(
      request: CheckoutRequest
    ) -> Checkout
      effects [CreateCheckout]

    observation events: Stream<PaymentEvent>
    simulator sandbox
    verifier signed_events
  }
}

A real extension format may look nothing like this. The contract is the point. It defines resources, operations, effect classes, retry rules, observations, a simulator, and a verifier. A Stripe adapter can implement that contract; a fake adapter can implement it for local tests. Stripe does not become syntax.

There should be three honest levels of integration:

  1. A semantic domain interface with a verified adapter.
  2. A typed foreign interface with declared effects and partial guarantees.
  3. An opaque escape hatch that explicitly disables some checks.

The third level is essential. A language that cannot call an npm package until a standards committee models it will not survive contact with a real repository. But the escape hatch should leave a visible hole in the guarantee surface rather than laundering unknown code into a trusted capability.

One program may need several views

The canonical representation need not be the best interface for every person or domain.

One semantic graph with many projectionsA canonical semantic graph in the center connects to model text, human review, generated TypeScript, a notebook, a spreadsheet view, and a debugger trace.Canonical semantic graphstable identities · typed relationshipsModel formHuman reviewGenerated TypeScriptNotebookSpreadsheet viewDebugger traceA projection is a synchronized view, not an independent copy of the program.
One semantic program may need several synchronized views. No single syntax is optimal for every author, reviewer, and runtime.

A model may prefer a redundant textual form. A reviewer may want a compact effect and resource summary. A compiler emits TypeScript. An ML researcher works in a notebook. A financial analyst edits a spreadsheet grid. A graphics engineer manipulates a scene. A debugger follows a causal trace.

Those can be projections over one semantic graph rather than files that drift independently. This is difficult: round-tripping visual edits without destructive serialization is an open problem. The alternative is already difficult. Today the notebook, generated script, deployment settings, and spreadsheet often disagree with no mechanism to say which one owns the truth.

The same core outside web development

The architecture is not general merely because it has an extension mechanism. It should survive one contrast.

experiment ImageClassifier {
  resource samples: Dataset<Image, Label> = datasets.cifar10()
  resource model: Model<Image, Label> = ml.resnet18()
  capability compute: Accelerator = accelerators.available_gpu()

  run training = ml.train {
    model = model
    dataset = samples
    optimizer = adam(learning_rate = 0.001)
    epochs = 20
    checkpoint = every(5 epochs)
  }

  observe [
    training.loss_curve,
    training.validation_accuracy,
    training.gradient_statistics,
    compute.memory_usage,
    training.sample_predictions
  ]

  expect {
    training.gradients.finite
    training.validation_accuracy >= 0.80
  }
}

This does not constitute an ML language. It shows the recurring core: resources, capabilities, effects, time, observations, assertions, and evidence. The ML extension supplies datasets, models, optimizers, checkpoints, and domain-specific observations. A PyTorch adapter supplies execution. A future compiler could choose another backend without pretending all backends are equivalent.

Architecture of a programming substrate for probabilistic programmersHuman intent passes through a probabilistic model, a canonical semantic program, deterministic compilation, running systems, runtime tools, structured observations, protected evaluation, and project knowledge. Knowledge loops back to the model. Domain extensions and concrete adapters connect to several stages. The active region for this article is highlighted.MAIN LOOPDOMAIN LAYERSHuman intentProbabilistic modelCanonical semantic programCompiler, planner, reconcilerExecutable code and external resourcesRuntime and manipulation toolsStructured observationsProtected evaluationProject knowledge and verified trajectoriesevidence-bearing feedbackDomain extensionstypes · effects · observationsConcrete adaptersproviders · backends · devicesprobabilistic generation or inferencedeterministic flow or control
Part 2 focuses on the canonical semantic program and the domain-extension layer that makes new concepts legible to compilers and runtimes.

The rule is simple: do not standardize every noun. Standardize how a new noun acquires types, effects, lifecycle, observations, verification, and a path to execution.

That still leaves the program cut off at the repository boundary. The deployed Storefront depends on provider instances, secrets, domains, webhooks, certificates, and settings that currently live in dashboards.

Previous: What Should Coding Agents Compile To?. Next: The Browser Is the Uncompiled Part of Your Program.