DEV Community

Cover image for Sub-Turing: All Good Programs Must Come to an End
Dirk Mattig
Dirk Mattig

Posted on

Sub-Turing: All Good Programs Must Come to an End

The decision to create a new purpose-built language instead of reusing an existing general-purpose language is deeply connected with a concept from theoretical computer science called Turing completeness. Loosely speaking, a programming language is Turing-complete if it can express any computation a computer could ever perform. Practically all general-purpose languages in use today have this capability. A necessary condition for Turing completeness is the ability to express unbounded looping, and this is where things get interesting in the context of agentic API-orchestration:

Do you want to hand an untrusted agent the ability to execute a program in your host application which will run forever?

I deliberately decided against this when designing the language and gave it a theoretical safeguard: Neander is Turing-incomplete. Every valid Neander program is guaranteed to come to an end. And any invalid program is not started in the first place anyway.

What is missing

Neander lacks the ability to express unbounded looping, and this inability comes in two flavors:

No recursion. A program has one entry point, main, and no way to turn around and re-enter the program. Also, it has no means to define functions of its own which it could then call. A call reaches only a registered API and returns. The call stack simply cannot grow without end.

Every loop is bounded. Neander has exactly two iteration constructs, each and repeat (there is no while), and neither can run away:

each walks a list or a map — a finite, immutable value that already exists, so the iteration count is fixed before the loop starts.

let confirmed: [Booking] =? call bookings.list(state: "confirmed")
let ids: [int] = each confirmed as b -> int {
  yield b.guestId
}
Enter fullscreen mode Exit fullscreen mode

repeat runs a block a counted number of times, and its ceiling is a literal you must write into the source:

repeat pageCount limit 20 as i {
  call orders.list(offset: i * 100, limit: 100)
}
Enter fullscreen mode Exit fullscreen mode

What is still missing

Are we safe now? In theory, yes. Unfortunately, systems have to reliably work in practice, not in theory.

Will a Neander program terminate?

Yes, guaranteed.

OK, but when, exactly?

Well, eventually.

And that is where reality bites you. In practice, eventually might be long enough to overload your host application and cause an incident. The same goes for the loops. They are bounded, yes, but where exactly does this bound sit? Lists could be huge and limit 1000000000 is valid syntax in Neander.

Something is still missing.

As we will find out next time, this something is of a very practical nature: Neander programs are living on a budget.

In the meantime, read the Neander spec, embed Grotto in your own app, and let me know how it ended.

Top comments (3)

Collapse
 
topstar_ai profile image
Luis

I found the concept of intentionally designing a language to be Turing-incomplete, like Neander, to be quite intriguing, particularly in the context of agentic API-orchestration where security and predictability are crucial. The trade-offs between expressiveness and safety are well-considered, and the bounded iteration constructs seem like a reasonable approach to preventing uncontrolled program execution. However, I'm interested in learning more about how the "budget" concept will help mitigate the potential issues with large lists and high repeat limits - will this involve some form of resource accounting or timeout mechanisms?

Collapse
 
newadventuresinit profile image
Dirk Mattig

Thank you so much for your interest in Neander!
To answer your question: in short, yes, both.
The budget system places hard upper bounds on a program's resource consumption in six different dimensions, where computation, memory, and time are the three most prevalent ones. The next blog post will cover this topic and it is due this Friday. If you prefer, you can also read the relevant section of the latest language specification:
github.com/newadventuresinit/neand...

If you have more questions or want to discuss the pros and cons of certain design decisions: you know where to find me!

Collapse
 
topstar_ai profile image
Luis

Thanks for the explanation, Dirk. The budget model sounds like a natural next layer after making the language sub-Turing.

I think this is an important distinction for agentic systems: termination guarantees solve the “can this run forever?” problem, but they do not automatically solve the “can this consume too many resources before finishing?” problem. A bounded loop over a massive dataset is still potentially dangerous without runtime accounting.

The six-dimensional budget approach sounds interesting because it moves safety from a theoretical property into an operational constraint. I’m especially curious whether the runtime will support dynamic budget estimation before execution — for example, analyzing the planned API calls and collection sizes before allowing an agent-generated program to run.

This kind of design feels closer to capability-based execution environments than traditional programming languages, where the goal is not just expressing computation but controlling what computation is allowed.