Yes. If you’re thinking about programming-language design specifically to make LLM-generated code more likely to be correct, there is a very interesting set of features. The key idea is:
Move as much of the correctness problem as possible from “the LLM has to remember to do it right” into “the language makes wrong programs difficult or impossible to express.”
Recent research supports this direction: type-constrained generation, for example, has been shown to substantially reduce compilation errors in LLM-generated TypeScript and improve functional correctness. (DOI)
Features I’d prioritize
| Language feature | What it prevents | Why it helps an LLM |
|---|---|---|
| Strong static types | Wrong kinds of values | Compiler catches many mistakes |
| Type inference | Verbose annotations | Gives the LLM safety without making code cumbersome |
| Algebraic data types | Invalid states | Forces explicit representation of alternatives |
| Exhaustive pattern matching | Forgotten cases | Compiler tells the LLM what it forgot |
| Option/Maybe types | Null-pointer errors | Makes absence explicit |
| Result/error types | Ignored errors | Forces error handling |
| Immutable-by-default data | Accidental mutation | Reduces hidden state |
| Ownership/borrowing | Lifetime/aliasing bugs | Compiler handles difficult reasoning |
| Effect types | Hidden I/O, mutation, exceptions | Makes side effects visible |
| Refinement types | Invalid values | Compiler can enforce properties such as x > 0 |
| Contracts/pre/postconditions | Violated assumptions | Converts requirements into checkable obligations |
| Units/types for quantities | Unit mismatches | Prevents e.g. meters + seconds |
| Restricted implicit conversions | Subtle coercion bugs | Makes transformations explicit |
| Simple, regular syntax | Syntax mistakes | Easier token prediction |
| One obvious idiom | API/design ambiguity | Reduces the number of plausible-but-wrong solutions |
| Deterministic semantics | Surprising behavior | Easier for the model to reason about |
| Compile-time verification | Logic errors | Gives the LLM immediate feedback |
The really interesting ones are the last few layers.
1. Algebraic data types + exhaustive matching
Instead of:
status = 0 // maybe pending
status = 1 // maybe success
status = 2 // maybe failureCode language: JavaScript (javascript)
you have:
Status =
| Pending
| Success(Data)
| Failure(Error)Code language: JavaScript (javascript)
Then:
match status:
Pending -> ...
Success(data) -> ...
Failure(err) -> ...
If the LLM adds another variant, the compiler can point out every place that needs updating.
This creates a very useful feedback loop for an LLM:
model makes change β compiler identifies consequences β model fixes consequences.
That is much better than expecting the model to mentally discover every consequence.
2. Refinement types
This is even more powerful.
Instead of:
age: IntCode language: HTTP (http)
you could have:
age: Int where 0 <= age <= 150Code language: HTTP (http)
or:
NonZeroInt
or:
Index(array.length)Code language: CSS (css)
Now the language carries some of the meaning of the program, rather than merely its data representation.
Refinement types explicitly add logical predicates to ordinary types, allowing the type checker to enforce properties about values. (DOI)
This is particularly attractive for LLMs because it turns:
“Remember that this value must never be zero.”
into:
“The compiler won’t accept a value that violates this.”
3. Contracts
You can go another step:
fn divide(a: Int, b: Int) -> Int
requires b != 0
ensures result * b == a
Now the LLM doesn’t have to reason solely from comments and function names.
The interface itself describes the correctness conditions.
Some newer language experiments explicitly combine contracts, refinement types, ADTs, and compiler verification for this purpose. (GitHub)
4. Explicit effects
This is an underrated one.
Imagine:
fn calculate(x: Int) -> Int
versus:
fn calculate(x: Int) -> Int
effects { IO, Database }
The second tells the LLM that calling this function isn’t just computationβit has externally visible consequences.
You could potentially distinguish:
pure
IO
network
filesystem
database
mutation
This makes action at a distance much harder.
Several newer AI-oriented languages are explicitly experimenting with this sort of design, including explicit effects, ownership, and algebraic errors. (GitHub)
The really interesting design principle
I would actually organize the language around a hierarchy:
Syntax
β
Types
β
Data invariants
β
Resource invariants
β
Effect invariants
β
Behavioral contracts
β
Tests / formal verification
At each level, the language gives the LLM more information and fewer ways to be wrong.
For example:
Int
is weak.
NonZeroInt
is stronger.
UserId
is stronger still.
ExistingUserId
is stronger.
And:
ExistingUserId
requires user.existsCode language: CSS (css)
starts getting into actual behavioral verification.
There’s another feature I’d put unusually high on the list
Make the compiler’s error messages extremely machine-readable.
For an LLM, this:
error[E042]:
expected: NonEmptyList<User>
found: List<User>
at: users[37]
reason: list may be empty
suggested fixes:
- check for empty list
- use `head?`
- establish `length > 0`Code language: PHP (php)
is vastly more useful than:
Type mismatch.
In other words, the compiler becomes part of the LLM’s reasoning system.
That’s an important distinction. You don’t necessarily need a language where the LLM magically writes perfect code. You want a language where:
the LLM can cheaply propose code, and the language can cheaply tell it exactly what is wrong.
Research on constrained decoding is moving in essentially this direction: rather than merely asking an LLM to generate valid code probabilistically, the generation process itself can be constrained by the language’s formal rules. One 2025 study found that type-constrained decoding reduced compilation errors by more than half. (DOI)
If I were designing an “LLM-native” language
I’d probably combine:
Rust
- ownership
- borrowing
- strong static types
Haskell/ML
- algebraic data types
- pattern matching
- type inference
Idris/Liquid Haskell-style systems
- dependent/refinement types
- proofs
Effect systems
- explicit side effects
Design by contract
- preconditions/postconditions
LLM-specific additions
- extremely regular syntax
- minimal number of equivalent idioms
- highly structured compiler errors
- machine-readable AST
- automatic test generation
- incremental compilation
- explicit interfaces
- APIs whose types encode as much semantic information as possible
There are already projects explicitly exploring this premiseβfor example, Almide describes itself as a statically typed language optimized around LLM code generation, while Promise is designed around explicit ownership, effects, errors, and “one obvious way” to express things. (GitHub)
The deeper idea is that programming languages for LLMs may want to optimize not for human typing speed, but for the ratio of semantic information conveyed per token and incorrect programs rejected by the compiler.
That’s a potentially very different programming-language design target from conventional languages.
Yes. Iβd expand the comparison to include the major mainstream languages plus several languages that are especially interesting from the βLLM-friendly language designβ perspective. The 2025 Stack Overflow survey includes Python, JavaScript, SQL, Java, C++, C#, TypeScript, PHP, Rust, Go, Lua, Kotlin, Swift, Ruby, Dart, and many others among its surveyed languages. (Stack Overflow Developer Survey)
Here is the table in the format you requested. βHas itβ means the language provides the feature in a meaningful built-in language/type-system form; βdoesn’tβ means it lacks it or requires a library/convention rather than the language enforcing it.
| Language features | Language has it | Language doesn’t have it |
|---|---|---|
| Strong static typing | Rust, C++, C#, Java, TypeScript, Go, Kotlin, Swift, Elm, Haskell, F#, OCaml, Scala, Dart, Zig | Python, JavaScript, Lua, Ruby, PHP |
| Type inference | Rust, C++, C#, Java, TypeScript, Go, Kotlin, Swift, Elm, Haskell, F#, OCaml, Scala, Dart, Zig | C, PHP, Lua, JavaScript |
| Algebraic data types | Rust, Elm, Haskell, F#, OCaml, Scala, Swift, Kotlin | C, C++, Java, C#, Go, Python, JavaScript, TypeScript, Lua, Ruby, PHP |
| Exhaustive pattern matching | Rust, Elm, Haskell, F#, OCaml, Scala, Swift, Kotlin | C, C++, Java, C#, Go, Python, JavaScript, TypeScript, Lua, Ruby, PHP |
| Option/Maybe type | Rust, Elm, Haskell, F#, OCaml, Scala, Swift, Kotlin, Java, C#, TypeScript | C, C++, Go, Python, JavaScript, Lua, Ruby, PHP |
| Result/error type | Rust, Elm, Haskell, F#, OCaml, Swift, Scala | C, C++, Java, C#, Go, Python, JavaScript, TypeScript, Lua, Ruby, PHP |
| Null safety enforced by type system | Rust, Elm, Kotlin, Swift, Dart, C#, TypeScript* | C, C++, Java, Go, Python, JavaScript, Lua, Ruby, PHP |
| Immutable-by-default | Elm, Haskell, F#, OCaml | Rust, C++, C#, Java, TypeScript, Go, Python, JavaScript, Lua, Ruby, PHP |
| Immutable data structures built into language | Elm, Haskell, Clojure | Rust, C, C++, Java, C#, Go, Python, JavaScript, Lua, Ruby, PHP |
| Ownership system | Rust | C, C++, C#, Java, TypeScript, Go, Python, JavaScript, Lua, Ruby, PHP |
| Borrow checker | Rust | C, C++, C#, Java, TypeScript, Go, Python, JavaScript, Lua, Ruby, PHP |
| Automatic memory management | Java, C#, Go, Python, JavaScript, TypeScript, Kotlin, Swift, Dart, Ruby, PHP, Lua, Haskell, Elm, Scala, Clojure | C, C++, Rust, Zig |
| Manual memory management | C, C++, Zig | Rust, Java, C#, Go, Python, JavaScript, TypeScript, Kotlin, Swift, Dart, Ruby, PHP, Lua, Elm, Haskell |
| Compile-time bounds/safety checking | Rust, Ada, SPARK | C, C++, C#, Java, Go, Python, JavaScript, TypeScript, Lua, Ruby, PHP |
| Refinement types | Liquid Haskell, F*, Idris, Ada/SPARK | C, C++, Rust, C#, Java, Go, Python, JavaScript, TypeScript, Lua, Ruby, PHP, Elm |
| Dependent types | Idris, Agda, Lean, Coq | C, C++, Rust, C#, Java, Go, Python, JavaScript, TypeScript, Lua, Ruby, PHP, Elm |
| Design-by-contract | Eiffel, Ada/SPARK, Dafny, Whiley | C, C++, Rust, C#, Java, Go, Python, JavaScript, TypeScript, Lua, Ruby, PHP, Elm |
| Formal verification facilities | Lean, Coq, Isabelle, F*, Dafny, SPARK, Idris | C, C++, Rust, C#, Java, Go, Python, JavaScript, TypeScript, Lua, Ruby, PHP, Elm |
| Compile-time assertions | C, C++, Rust, C#, D, Swift, Zig | Python, JavaScript, TypeScript, Lua, Ruby, PHP, Java, Go |
| Unit/dimension types | F#, Ada, Haskell libraries, Rust libraries, Scala libraries | C, C++, Java, C#, Go, Python, JavaScript, TypeScript, Lua, Ruby, PHP, Elm |
| Explicit effects system | Haskell, Koka, Eff, Unison, OCaml (effect handlers) | C, C++, Rust, C#, Java, Go, Python, JavaScript, TypeScript, Lua, Ruby, PHP, Elm |
| Explicit exception/effect distinction | Haskell, Koka, Eff | C, C++, Java, C#, Python, JavaScript, TypeScript, Ruby, PHP, Go, Rust |
| Pure functions encouraged/enforced | Haskell, Elm | C, C++, Rust, C#, Java, Go, Python, JavaScript, TypeScript, Lua, Ruby, PHP |
| Referential transparency | Haskell, Elm | C, C++, Rust, C#, Java, Go, Python, JavaScript, TypeScript, Lua, Ruby, PHP |
| Restricted implicit conversions | Rust, Go, Ada, Haskell, Elm | C, C++, JavaScript, PHP, Python, Ruby |
| No implicit null values | Rust, Elm | C, C++, Java, C#, Go, Python, JavaScript, TypeScript, Lua, Ruby, PHP |
| Exhaustive enum handling | Rust, Swift, Kotlin, Elm, Haskell, F#, OCaml | C, C++, Java, C#, Go, Python, JavaScript, TypeScript, Lua, Ruby, PHP |
| Explicit visibility/access control | C++, C#, Java, Rust, Kotlin, Swift, Go, Python, TypeScript, Ruby, PHP | Lua, JavaScript |
| Structural typing | TypeScript, Go | C, C++, Rust, Java, C#, Kotlin, Swift |
| Nominal typing | Rust, C++, Java, C#, Kotlin, Swift, Go, Haskell, Elm | TypeScript, Python, JavaScript, Lua, Ruby |
| Interfaces/traits/typeclasses | Rust, Haskell, Go, Java, C#, TypeScript, Swift, Kotlin, Scala, F# | C, C++, Python, JavaScript, Lua, Ruby, PHP |
| Generics | C++, Rust, Java, C#, Go, TypeScript, Swift, Kotlin, Scala, Haskell, F#, Dart | C, Python, JavaScript, Lua, Ruby, PHP |
| Compile-time metaprogramming | C++, Rust, Haskell, Scala, Lisp/Clojure, Nim, Zig | Java, C#, Go, Python, JavaScript, TypeScript, Lua, Ruby, PHP |
| Macros | C, C++, Rust, Lisp/Clojure, Elixir, Scala, Nim | Java, C#, Go, Python, JavaScript, TypeScript, Lua, Ruby, PHP |
| Hygienic macros | Rust, Scheme/Racket, Clojure, Elixir | C, C++, Java, C#, Go, Python, JavaScript, TypeScript, Lua, Ruby, PHP |
| Algebraic effects | Koka, Eff, Unison, OCaml | C, C++, Rust, Java, C#, Go, Python, JavaScript, TypeScript, Lua, Ruby, PHP |
| Actor/concurrency model | Erlang, Elixir, Pony, Scala/Akka | C, C++, Rust, Java, C#, Go, Python, JavaScript, TypeScript, Lua, Ruby, PHP |
| Lightweight concurrency primitives | Go, Erlang, Elixir, Kotlin, Rust, C#, Java | C, C++, Python, JavaScript, TypeScript, Lua, Ruby, PHP |
| Compile-time race/data-race prevention | Rust | C, C++, Java, C#, Go, Python, JavaScript, TypeScript, Lua, Ruby, PHP |
| Memory safety enforced by compiler | Rust, Ada/SPARK, Swift (in significant respects) | C, C++, Go, Java, C#, Python, JavaScript, TypeScript, Lua, Ruby, PHP |
| Deterministic destruction/resource management | C++, Rust, Swift | Java, C#, Go, Python, JavaScript, TypeScript, Lua, Ruby, PHP |
| RAII | C++, Rust | C, Java, C#, Go, Python, JavaScript, TypeScript, Kotlin, Swift, Lua, Ruby, PHP |
| Linear/affine types | Rust, Haskell extensions, Clean, Linear Haskell | C, C++, Java, C#, Go, Python, JavaScript, TypeScript, Lua, Ruby, PHP |
| Units of ownership/resources tracked by type system | Rust, Linear Haskell, Clean | C, C++, Java, C#, Go, Python, JavaScript, TypeScript, Lua, Ruby, PHP |
| Compiler-enforced initialization | Rust, C#, Java, Kotlin, Swift, Go | C, C++, Python, JavaScript, Lua, Ruby, PHP |
| Compiler-enforced unreachable-state detection | Rust, Elm, Haskell, F#, OCaml | C, C++, Java, C#, Go, Python, JavaScript, TypeScript, Lua, Ruby, PHP |
| First-class functions | Python, JavaScript, TypeScript, Rust, C++, C#, Java, Go, Kotlin, Swift, Elm, Haskell, F#, OCaml, Scala, Ruby, Lua, PHP | C |
| Lambda expressions | C++, Rust, C#, Java, Go, Python, JavaScript, TypeScript, Kotlin, Swift, Elm, Haskell, F#, OCaml, Scala, Ruby, Lua, PHP | C |
| Simple/regular syntax | Go, Elm, Lua, Python, Haskell | C++, C, Rust, Scala, C#, Java, JavaScript, PHP |
| Single obvious idiom encouraged by language | Go, Elm, Python | C++, C, JavaScript, PHP, Perl |
| Machine-readable AST/tooling | Most modern languages | β |
| Compiler diagnostics designed for automated tooling | Rust, Elm, Haskell, TypeScript, Swift, Kotlin, modern C#/Java | Older C/C++ toolchains and many dynamically typed languages |
| Static analysis ecosystem | Rust, C++, C#, Java, TypeScript, Go, Kotlin, Swift, Python | Lua, PHP, Ruby and others have varying degrees |
| Language-server/type-aware tooling | Rust, C++, C#, Java, TypeScript, Go, Kotlin, Swift, Python, Elm | Some smaller/older languages have less mature tooling |
* TypeScript’s strictNullChecks can provide strong null checking, but JavaScript interoperability and escape hatches mean it isn’t equivalent to Rust/Elm’s guarantees.
The languages I’d include in the experiment
For what you’re investigating, I’d divide them into four groups:
Mainstream languages
- Python
- JavaScript
- TypeScript
- C
- C++
- Java
- C#
- Go
- Rust
- Swift
- Kotlin
- PHP
- Ruby
- Dart
- Lua
Functional/type-system languages
- Elm
- Haskell
- F#
- OCaml
- Scala
- Clojure
- Elixir
- Erlang
Systems/newer languages
- Zig
- Nim
- D
- Crystal
Languages specifically interesting for extreme correctness
- Ada/SPARK
- Idris
- Lean
- Coq
- F*
- Dafny
- Liquid Haskell
- Koka
- Pony
The distinction is important because the most popular languages aren’t necessarily the most useful ones for investigating LLM-oriented language design. For example, Rust is especially interesting because its ownership and borrow-checking system gives the compiler substantial ability to reject incorrect programs; Stack Overflow’s 2025 survey also reports Rust as the most admired programming language. (Stack Overflow Developer Survey)
If our ultimate goal is to answer βWhich language features actually make LLM-generated programs more correct?β, I’d narrow the next table to perhaps 25β30 features and put each language as a column. That would produce a much more useful matrix:
| Feature | Python | Rust | C++ | TypeScript | JavaScript | Elm | Go | … |
|---|---|---|---|---|---|---|---|---|
| Static typing | β | β | β | β | β | β | β | … |
| Algebraic types | β | β | β | β | β | β | β | … |
| Exhaustive matching | β | β | β | β | β | β | β | … |
| Ownership | β | β | β | β | β | β | β | … |
| Refinement types | β | β | β | β | β | β | β | … |
| Contracts | β | β | β | β | β | β | β | … |
| Effect system | β | β | β | β | β | β | β | … |
| … |
That matrix would let us do something particularly interesting: look for combinations of features that give an LLM the strongest compiler feedback loop and therefore potentially make an LLM-native programming language substantially more reliable.