Tech News
← Back to articles

LLMs could be, but shouldn't be compilers

read original related products more articles

I’ve been going round and round in my mind about a particular discussion around LLMs: are they really similar to compilers? Are we headed toward a world where people don’t look at the underlying code for their programs?

People have been making versions of this argument since Andrej Karpathy’s “English is the hottest new programming language.” Computer science has been advancing language design by building higher and higher level languages; this is the latest iteration: maybe we no longer need a separate language to express ourselves to machines; we can just use our native tongues (let alone English).

My stance has been pretty rigid for some time: LLMs hallucinate, so they aren’t reliable building blocks. If you can’t rely on the translation step, you can’t treat it as a serious abstraction layer because it provides no stable guarantees about the underlying system.

As models get better, hallucinations become less central (even though models still make plenty of mistakes). Lately I’ve been thinking about a different question: imagine an LLM that never “hallucinates” in the usual sense, one that reliably produces some plausible implementation of what you asked. Would that make it the next generation of compiler? And what would that mean for programming and software engineering in general?

This post is my stab at that question. The core of my argument is simple:

Specifying systems is hard; and we are lazy.

Before getting to what that means in practice, I want to pin down something else: what does it mean for a language to be “higher level”?

Programming is, at a fundamental level, the act of making a computer do something. Computers are very dumb from the point of view of a human. You need to tell the computer exactly what to do, there's no inference. A computer fundamentally doesn't even have the notion of a value, type, concept; everything is a series of bits, which are processed to generate other bits, we bring meaning to this whole ordeal. Very early on, people have started by building arithmetic and logical instructions into computers, you would have 2 different bit sequences each denoting a number, you could add, subtract, multiply them. In order to make a computer do something, you could denote your data in terms of a bunch of numbers, map your logical operations onto those ALU instructions, and interpret the result in your domain at the end. Then, you can define a bunch of operations on your domain, which will be compiled down to those smaller ALU instructions, and voila, you have a compiler at hand.

This compiler is, admittedly, kind of redundant. It doesn't do anything you would be able to do because you essentially have a direct mapping between your two languages, your higher level language desugars into a bunch of lower level ALU instructions, so anyone would be able to implement the same mapping very easily, and even go further, perhaps just write the ALU instructions themselves.

What real higher level languages do is they give you an entirely new language that is eventually mapped to the underlying instruction set in non-trivial mechanisms in order to reduce the mental complexity on the side of the programmer. For instance, instruction sets do not have the concept of variables, nor loops, nor data structures. You can definitely build a sequence of instructions that amount to a binary search tree, but the mental burden of the process is orders of magnitude higher than any classic programming language. Structs, Enums, Classes, Loops, Conditionals, Exceptions, Variables, Functions are all properties that exist in higher level languages that are compiled away when going down the stack.

... continue reading