Remix.run Logo
xscott 14 hours ago

> would you rather review LLM-written assembly or LLM-written Haskell?

I wish we had a language that was targeted specifically for LLMs to write and humans and LLMs to inspect:

- Simple robust syntax

- One obvious way to do things

- Static type checking

- Purely functional encouraged, escape hatches for performance

- Inspect-able, testable, and reviewable in small pieces

- Something like formal predicates, preconditions, post-conditions, assertions, or effects typing

Giving LLMs all the surface area of Python, JavaScript, TypeScript, or C++ seems like a huge mistake. It's amazing it works as well as it does. Well written Haskell is beautiful, but there are way too many ways to write Haskell:

https://people.willamette.edu/~fruehr/haskell/evolution.html

ulrikrasmussen 13 hours ago | parent | next [-]

I don't really understand why we need an LLM-specific programming language - all of the properties you list are properties that modern programming language designers already try to achieve.

> - Inspect-able, testable, and reviewable in small pieces

This is more a property of the architecture than a property of the language, although some programming languages support it better than others.

NortySpock 14 hours ago | parent | prev | next [-]

Gleam ?

Elixir's exhaustive pattern matching and gradual type checking is also on my radar.

I guess I don't know what you mean by effects typing.

B-Con 14 hours ago | parent | prev | next [-]

Go seems like a pragmatic fit, even though it was obviously not designed for LLMs and nor is it purely functional.

I review a lot of Go code, which means I review a lot of LLM Go code. Even though the human vs LLM authorship distinction has strong signals, Go's simple nature seems like a useful constraint on how LLMs can express themselves.

jbreckmckye 13 hours ago | parent | next [-]

- Too verbose, too much noise e.g. error handling.

- Type system is too concrete, can't express sets or unions.

- Not much support for functional programming except passing closures

- Can't express immutability. Well, there's const, but it's crippled

These things mean Go falls short of what GP wants.

But Go has very distinct and (honestly kinda weird) design goals, it isn't really supposed to be "the simple applications language". It's supposed to be "C with NewSqueak", very much a systems programming language. You see that in its aggressively concrete type system

actionfromafar 13 hours ago | parent | prev [-]

I agree it's easy to read. But you also have to read a lot of it, before anything interesting happens.

lelanthran 10 hours ago | parent | prev | next [-]

> I wish we had a language that was targeted specifically for LLMs to write and humans and LLMs to inspect:

How would this help? LLMs are fine with existing languages' syntax, semantics, idioms, etc. Where they fall over is in understanding intent of a prompt within the context of architecture, systems, roadmap, etc.

IsTom 13 hours ago | parent | prev | next [-]

> escape hatches for performance

These allow the model to cheat in ways that are harder to detect. In actually pure FP you can be sure that it's not counting function invocations to bypass tests.

xscott 13 hours ago | parent [-]

Yeah, but sorting algs, FFTs, matrix factorizations, backprop, and many other things just aren't the same with purely functional data structures.

Maybe these well known cases could be hidden in the API, but it's easy to come up with other examples. I forget what Clojure calls it, but they have some notion about things where they're mutable during "birth" and then locked down.

Someone smarter than me knows how to do this right.

IsTom 11 hours ago | parent [-]

That sounds like Haskell's ST monad and yeah, that could work. These are very specific "escape hatches" though and typically people want more.

xscott 10 hours ago | parent [-]

As far as I can tell, the good models will use whatever you give them. So it seems we should only give them language features that help humans understand and maintain what the model writes.

Amusingly, a friend had Claude write some BrainF*ck the other day. Non-trivial algorithm, and it made working code on the first try.

sriku 13 hours ago | parent | prev | next [-]

Lean seems to fit your ask fairly well.

empty_set35204 13 hours ago | parent | prev | next [-]

I think ocaml is fairly close to this to be honest.

avadodin 12 hours ago | parent | prev | next [-]

I always find it amazing that they can produce syntactically valid Python code —which is basically Whitespace with some syntactic sugar— most of the time while being utterly unable to count the rs in strawberry.

reichstein 12 hours ago | parent | prev [-]

LLMs to inspect:

- Simple robust syntax

> - One obvious way to do things

That always leads to more verbosity. Much syntactic sugar is a more specialized way to do a subset of a more general thing. Why have `a + b` when you could just write `a.add(b)`? Because it's easier to read. The general functionality needs to account for all cases, the specialized one can cut that down just the things that matter to a specific common use case.

- Static type checking

\<meme>Which one?\</meme>

That's a very, very deep can of worms. One could argue that of an LLM is generating the code, then the type system doesn't need to be understandable to humans, so throw in all the features you'd ever want and just have the LLM change the code if it's not valid. Unions of higher order generic functions, sure! Or one could argue that there should be minimal magic, because the LLM understands the language only by its source, so everything should be explicit. If the LLM can prove that something is sounds to the compiler, accept it. Give ways to give extra evidence of soundness, like declaring invariants and contacts.

- Purely functional encouraged, escape hatches for performance

"One obvious way to do things", except when you need two. Purely functional except when it matters.

Why doesn't performance always matter? (And how will an LLM know if it does?)

Being purely functional is nice for data, but not for data structures that are updated in place. If all you do is stream data from one DB query into another, then your mutable state is the database. Otherwise might as well accept that it's a multiparadigmatic language with both imperative, functional and OO features. Just like all the others.

- Inspect-able, testable, and reviewable in small pieces

Good modularity and abstraction. No global scope. Maybe something like dependency injection to decouple from dependencies? (That does not make code readable, though.)

- Something like formal predicates, preconditions, post-conditions, assertions, or effects typing

That! LLMs look at the source. The more explicit the source is, the less it has to infer from context or existing knowledge. If the LLM can create its own predicates, accepted by the static type/analysis system, to prove that it's code is sounds, that allows more flexibility than having to fit into any fixed type system. Do we want or need that flexibility? Maybe. Most LLM-generated code is directly inspired by existing idiomatic code in the same language. Some is translated from other languages, trying to match up idioms. Some is just blindly trying to make unit tests pass. We could end up with generated predicates tailored to specific unit tests, not the actual concept.