27 October 2026

Loops became while, decision models became if. Which primitives are next?

  • machine learning
  • programming

A primitive is one of the small pieces a language gives you for free: if, while, ==, sort. Everything else is built out of them.

I keep coming back to one thought about AI. Loop engineering, where you put an agent on repeat until a goal is met, is a while loop with a model inside. And decision models like Jev are an if.

Jev came out in September from a startup called TypeSafe. You send it some text and a typed question, and it sends back a choice with probabilities. Within weeks OpenAI and AWS had their own versions. Its whole job is to be a branch in your code.

If while and if have models now, what about the rest of the language?

From keyword to model: while and if already have one, try/catch doesn't yet

Most primitives already have a model

Take a support inbox with a few hundred emails a day. Some are complaints, some ask the same question as yesterday, a few are urgent. Each step of handling them is a primitive, and most steps already have a model.

PrimitiveQuestion in the inboxModel that answers it
ifIs this email a complaint?decision models like Jev
switchShould a cheap or an expensive LLM write the reply?routers like RouteLLM
==Is this the same question as one we already answered?embedding similarity
sortWhich ticket goes first?rerankers
castWhat's the order number in this message?extraction models like NuExtract
assertIs the reply safe to send?guard models like Llama Guard
breakIs the agent writing the reply going in circles?stop models like StopLoss, out this month
memoizationCan we reuse the answer we gave yesterday?semantic caching

Most of these models are small, and most are older than Jev. What Jev changed is how it's sold: as a general primitive, typed and cheap, instead of a tool for one job.

The functional version is even cleaner

A yes/no question to Jev is a predicate, so it can go straight into a filter:

filter(emails, decide("is this a complaint?"))

Sorting works the same way. A reranker scores each ticket against a question, and you sort by that score:

sortBy(tickets, rank("how urgent is this?"))

Jev's multiple choice does the job of pattern matching. And libraries like LOTUS already let you run map, filter and reduce with a model doing the work inside.

Equality has the biggest knock-on effect. In the inbox, "the same question" now means "close enough", decided by an embedding and a threshold. So the cache will sometimes hand back the answer to a slightly different question. In one study a few percent of cache hits were wrong. Dedup, joins and sorting inherit the same looseness.

Fuzzy equality spreads to caching, dedup, joins and sorting

What's still missing

The gaps in the inbox look like the next models to arrive:

  • try / catch: the reply fails to send, and a model reads the error and picks the recovery. Agents are trained to recover from errors, but nobody ships this as its own model yet.
  • An optional return, like null or Optional: some emails can't be classified. A model whose only job is to say "I don't know" would return nothing instead of a guess.
  • A result type, like Rust's Result: the model returns either an answer or a clear error, so a wrong answer never slips through looking like a right one.
  • Higher-order functions: a model that returns a function, like writing the prompt or the code for the next step.

If decision models are any guide, these will arrive as small, typed models you call from ordinary code.

Primitives and the models behind them: nine have one, four don't yet

What changes when a primitive is a model

Ask decide whether an email is a complaint and it might say 0.83. Whether that counts as a complaint depends on your threshold. Set it at 0.7 and the email goes to the complaints queue. Set it at 0.9 and it doesn't. Every model-made primitive has a number like this. It's the new magic constant in your code.

A classic if is true or false; a model-made if is a probability and a threshold you pick

Testing changes with it. One email proves nothing about the filter. You need a few hundred labeled emails, and you check calibration: when the model says 80%, is it right about 80% of the time? The test suite becomes a dataset.

Purity goes too. Send the same email twice and you can get a different branch, even with a fixed seed. That's fixable at the inference level, but it isn't the default.

Where I'd stop

Some decisions shouldn't be a model call at all. A risk assessment generated from a codebase only sees the code. You also need the developers, the business people and whoever owns security. The uncertainty is in the problem itself, so context and output matter as much as input.

Models will take over primitives where a messy question has a small set of answers. Where the answer depends on people, they stay out of the branch.

I can see decide becoming a keyword like any other, and while (!decide("is the task done?", state)) reading as normal as while (i < n). while and if got their models first. The open question is which keyword is next.

Sources