ChatGPT: Language Features that Help LLMs

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 featureWhat it preventsWhy it helps an LLM
Strong static typesWrong kinds of valuesCompiler catches many mistakes
Type inferenceVerbose annotationsGives the LLM safety without making code cumbersome
Algebraic data typesInvalid statesForces explicit representation of alternatives
Exhaustive pattern matchingForgotten casesCompiler tells the LLM what it forgot
Option/Maybe typesNull-pointer errorsMakes absence explicit
Result/error typesIgnored errorsForces error handling
Immutable-by-default dataAccidental mutationReduces hidden state
Ownership/borrowingLifetime/aliasing bugsCompiler handles difficult reasoning
Effect typesHidden I/O, mutation, exceptionsMakes side effects visible
Refinement typesInvalid valuesCompiler can enforce properties such as x > 0
Contracts/pre/postconditionsViolated assumptionsConverts requirements into checkable obligations
Units/types for quantitiesUnit mismatchesPrevents e.g. meters + seconds
Restricted implicit conversionsSubtle coercion bugsMakes transformations explicit
Simple, regular syntaxSyntax mistakesEasier token prediction
One obvious idiomAPI/design ambiguityReduces the number of plausible-but-wrong solutions
Deterministic semanticsSurprising behaviorEasier for the model to reason about
Compile-time verificationLogic errorsGives 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 featuresLanguage has itLanguage doesn’t have it
Strong static typingRust, C++, C#, Java, TypeScript, Go, Kotlin, Swift, Elm, Haskell, F#, OCaml, Scala, Dart, ZigPython, JavaScript, Lua, Ruby, PHP
Type inferenceRust, C++, C#, Java, TypeScript, Go, Kotlin, Swift, Elm, Haskell, F#, OCaml, Scala, Dart, ZigC, PHP, Lua, JavaScript
Algebraic data typesRust, Elm, Haskell, F#, OCaml, Scala, Swift, KotlinC, C++, Java, C#, Go, Python, JavaScript, TypeScript, Lua, Ruby, PHP
Exhaustive pattern matchingRust, Elm, Haskell, F#, OCaml, Scala, Swift, KotlinC, C++, Java, C#, Go, Python, JavaScript, TypeScript, Lua, Ruby, PHP
Option/Maybe typeRust, Elm, Haskell, F#, OCaml, Scala, Swift, Kotlin, Java, C#, TypeScriptC, C++, Go, Python, JavaScript, Lua, Ruby, PHP
Result/error typeRust, Elm, Haskell, F#, OCaml, Swift, ScalaC, C++, Java, C#, Go, Python, JavaScript, TypeScript, Lua, Ruby, PHP
Null safety enforced by type systemRust, Elm, Kotlin, Swift, Dart, C#, TypeScript*C, C++, Java, Go, Python, JavaScript, Lua, Ruby, PHP
Immutable-by-defaultElm, Haskell, F#, OCamlRust, C++, C#, Java, TypeScript, Go, Python, JavaScript, Lua, Ruby, PHP
Immutable data structures built into languageElm, Haskell, ClojureRust, C, C++, Java, C#, Go, Python, JavaScript, Lua, Ruby, PHP
Ownership systemRustC, C++, C#, Java, TypeScript, Go, Python, JavaScript, Lua, Ruby, PHP
Borrow checkerRustC, C++, C#, Java, TypeScript, Go, Python, JavaScript, Lua, Ruby, PHP
Automatic memory managementJava, C#, Go, Python, JavaScript, TypeScript, Kotlin, Swift, Dart, Ruby, PHP, Lua, Haskell, Elm, Scala, ClojureC, C++, Rust, Zig
Manual memory managementC, C++, ZigRust, Java, C#, Go, Python, JavaScript, TypeScript, Kotlin, Swift, Dart, Ruby, PHP, Lua, Elm, Haskell
Compile-time bounds/safety checkingRust, Ada, SPARKC, C++, C#, Java, Go, Python, JavaScript, TypeScript, Lua, Ruby, PHP
Refinement typesLiquid Haskell, F*, Idris, Ada/SPARKC, C++, Rust, C#, Java, Go, Python, JavaScript, TypeScript, Lua, Ruby, PHP, Elm
Dependent typesIdris, Agda, Lean, CoqC, C++, Rust, C#, Java, Go, Python, JavaScript, TypeScript, Lua, Ruby, PHP, Elm
Design-by-contractEiffel, Ada/SPARK, Dafny, WhileyC, C++, Rust, C#, Java, Go, Python, JavaScript, TypeScript, Lua, Ruby, PHP, Elm
Formal verification facilitiesLean, Coq, Isabelle, F*, Dafny, SPARK, IdrisC, C++, Rust, C#, Java, Go, Python, JavaScript, TypeScript, Lua, Ruby, PHP, Elm
Compile-time assertionsC, C++, Rust, C#, D, Swift, ZigPython, JavaScript, TypeScript, Lua, Ruby, PHP, Java, Go
Unit/dimension typesF#, Ada, Haskell libraries, Rust libraries, Scala librariesC, C++, Java, C#, Go, Python, JavaScript, TypeScript, Lua, Ruby, PHP, Elm
Explicit effects systemHaskell, Koka, Eff, Unison, OCaml (effect handlers)C, C++, Rust, C#, Java, Go, Python, JavaScript, TypeScript, Lua, Ruby, PHP, Elm
Explicit exception/effect distinctionHaskell, Koka, EffC, C++, Java, C#, Python, JavaScript, TypeScript, Ruby, PHP, Go, Rust
Pure functions encouraged/enforcedHaskell, ElmC, C++, Rust, C#, Java, Go, Python, JavaScript, TypeScript, Lua, Ruby, PHP
Referential transparencyHaskell, ElmC, C++, Rust, C#, Java, Go, Python, JavaScript, TypeScript, Lua, Ruby, PHP
Restricted implicit conversionsRust, Go, Ada, Haskell, ElmC, C++, JavaScript, PHP, Python, Ruby
No implicit null valuesRust, ElmC, C++, Java, C#, Go, Python, JavaScript, TypeScript, Lua, Ruby, PHP
Exhaustive enum handlingRust, Swift, Kotlin, Elm, Haskell, F#, OCamlC, C++, Java, C#, Go, Python, JavaScript, TypeScript, Lua, Ruby, PHP
Explicit visibility/access controlC++, C#, Java, Rust, Kotlin, Swift, Go, Python, TypeScript, Ruby, PHPLua, JavaScript
Structural typingTypeScript, GoC, C++, Rust, Java, C#, Kotlin, Swift
Nominal typingRust, C++, Java, C#, Kotlin, Swift, Go, Haskell, ElmTypeScript, Python, JavaScript, Lua, Ruby
Interfaces/traits/typeclassesRust, Haskell, Go, Java, C#, TypeScript, Swift, Kotlin, Scala, F#C, C++, Python, JavaScript, Lua, Ruby, PHP
GenericsC++, Rust, Java, C#, Go, TypeScript, Swift, Kotlin, Scala, Haskell, F#, DartC, Python, JavaScript, Lua, Ruby, PHP
Compile-time metaprogrammingC++, Rust, Haskell, Scala, Lisp/Clojure, Nim, ZigJava, C#, Go, Python, JavaScript, TypeScript, Lua, Ruby, PHP
MacrosC, C++, Rust, Lisp/Clojure, Elixir, Scala, NimJava, C#, Go, Python, JavaScript, TypeScript, Lua, Ruby, PHP
Hygienic macrosRust, Scheme/Racket, Clojure, ElixirC, C++, Java, C#, Go, Python, JavaScript, TypeScript, Lua, Ruby, PHP
Algebraic effectsKoka, Eff, Unison, OCamlC, C++, Rust, Java, C#, Go, Python, JavaScript, TypeScript, Lua, Ruby, PHP
Actor/concurrency modelErlang, Elixir, Pony, Scala/AkkaC, C++, Rust, Java, C#, Go, Python, JavaScript, TypeScript, Lua, Ruby, PHP
Lightweight concurrency primitivesGo, Erlang, Elixir, Kotlin, Rust, C#, JavaC, C++, Python, JavaScript, TypeScript, Lua, Ruby, PHP
Compile-time race/data-race preventionRustC, C++, Java, C#, Go, Python, JavaScript, TypeScript, Lua, Ruby, PHP
Memory safety enforced by compilerRust, Ada/SPARK, Swift (in significant respects)C, C++, Go, Java, C#, Python, JavaScript, TypeScript, Lua, Ruby, PHP
Deterministic destruction/resource managementC++, Rust, SwiftJava, C#, Go, Python, JavaScript, TypeScript, Lua, Ruby, PHP
RAIIC++, RustC, Java, C#, Go, Python, JavaScript, TypeScript, Kotlin, Swift, Lua, Ruby, PHP
Linear/affine typesRust, Haskell extensions, Clean, Linear HaskellC, C++, Java, C#, Go, Python, JavaScript, TypeScript, Lua, Ruby, PHP
Units of ownership/resources tracked by type systemRust, Linear Haskell, CleanC, C++, Java, C#, Go, Python, JavaScript, TypeScript, Lua, Ruby, PHP
Compiler-enforced initializationRust, C#, Java, Kotlin, Swift, GoC, C++, Python, JavaScript, Lua, Ruby, PHP
Compiler-enforced unreachable-state detectionRust, Elm, Haskell, F#, OCamlC, C++, Java, C#, Go, Python, JavaScript, TypeScript, Lua, Ruby, PHP
First-class functionsPython, JavaScript, TypeScript, Rust, C++, C#, Java, Go, Kotlin, Swift, Elm, Haskell, F#, OCaml, Scala, Ruby, Lua, PHPC
Lambda expressionsC++, Rust, C#, Java, Go, Python, JavaScript, TypeScript, Kotlin, Swift, Elm, Haskell, F#, OCaml, Scala, Ruby, Lua, PHPC
Simple/regular syntaxGo, Elm, Lua, Python, HaskellC++, C, Rust, Scala, C#, Java, JavaScript, PHP
Single obvious idiom encouraged by languageGo, Elm, PythonC++, C, JavaScript, PHP, Perl
Machine-readable AST/toolingMost modern languagesβ€”
Compiler diagnostics designed for automated toolingRust, Elm, Haskell, TypeScript, Swift, Kotlin, modern C#/JavaOlder C/C++ toolchains and many dynamically typed languages
Static analysis ecosystemRust, C++, C#, Java, TypeScript, Go, Kotlin, Swift, PythonLua, PHP, Ruby and others have varying degrees
Language-server/type-aware toolingRust, C++, C#, Java, TypeScript, Go, Kotlin, Swift, Python, ElmSome 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:

FeaturePythonRustC++TypeScriptJavaScriptElmGo…
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.

admin
Author: admin

This is the server’s system administrator. This site is undergoing some changes.