Strona główna / Artykuły / Why Compiler Theory Still Matters When AI Writes the Code For You

Artykuł opublikowany po angielsku.

Why Compiler Theory Still Matters When AI Writes the Code For You

AI can generate a parser in minutes, but it won't tell you when you need one. See how small languages and deterministic rules beat prompts, and why you're the bottleneck.

1253 słów

AI can write a compiler on request in a few minutes. So is compiler theory finished as a subject? Not at all. Building the thing was never the hard part. The hard part is noticing you need one, and a model won't tell you.

An old argument with a new excuse

Universities have been trimming compiler courses from their syllabi for a while. The case against them sounds reasonable: writing a compiler is niche knowledge, needed in only a few jobs, so most programmers will never write one in their whole careers.

The counterpoint is that writing a C or JavaScript compiler is one thing, while everything compiler theory lets you do is another. Treating it as only useful for building programming languages undersells it. The same ideas power heavy text processing, and they also make code simpler and easier to read.

Now AI has given the dismissive side fresh ammunition. A model knows the whole theory and will generate the parsing code for you, so why learn any of it? Is that a real argument, or just an excuse?

Uses nobody asks you about

Take a small calculator exercise for students, where the tests are supplied and the learner implements the behavior. There are dozens of cases, and each test looks something like this:

test('Simple addition', () => {
  const calculator = new Calculator();
  calculator.press(4);
  calculator.addition();
  calculator.press(2);
  calculator.equals();
  expect(calculator.getDisplay()).equals('6');
});

It reads fine in isolation, but it's tedious to follow. Multiply it by forty and you lose track of what's covered and what isn't. A little compiler thinking lets you write one helper that plays the user and checks everything, so a test shrinks to a line:

test('Simple addition', () => {
  runCase('4 + 2 = [6]');
});

That's far more readable. It goes further, too. Instead of forty test blocks you can write a table where each line is a case, with the expected display in brackets and a comment saying what it proves:

test.each([
  '[0]                # display starts at zero',
  '1 [1]              # pressing a digit shows it',
  '12 [12]            # digits concatenate',
  '4 + 2 = [6]        # a basic sum',
  '5 × 3 = [15]       # product works the same',
  '7 - 9 = [-2]       # negative results',
  '9 ÷ 0 = [Error]    # division by zero errors',
  '2 + 3 × 4 = [14]   # respects precedence',
])('%s', runCase);

Some cases are trimmed here, but you can see the difference. Imagine reviewing the same coverage written out case by case.

Another place this pays off is wiring dependencies between modules. Done in code, you'd write something like this:

const system = new System();
system.createModule('A');
system.createModule('B');
system.createModule('C');
system.createModule('D');
system.connect('A', 'B');
system.connect('A', 'C');
system.connect('B', 'C');
system.connect('C', 'D');

With a tiny bit of language design you'd write this instead:

const system = compileSystem(`
  A -> B -> C -> D
  A      -> C
`);

With the first version you often need pencil and paper to rebuild the graph in your head. With the second, you simply see the graph. Implementing it is almost embarrassingly simple: split into lines, then split each line on the arrow.

Plenty more examples exist, and sometimes you don't even have to write the compiler because one exists already. A lot of configuration can just be YAML. An entity component system of the kind common in games grows and adapts easily when entities are described like this:

goblin:
  extends: enemy
  health: 30
  damage: 5
  ai: aggressive
  drops: [gold, dagger]
  resistances: [poison]

Before moving on, ask how many of these ideas a model volunteers when you request some code. In practice, almost none.

AI makes it more necessary

You could argue AI is the ultimate compiler: it understands nearly any language and can interpret it. There's one catch. It doesn't always get it right. That's among the biggest problems for anyone automating repetitive work with AI, because the output isn't deterministic. Today it does the job well, tomorrow it makes a mess.

So you end up working between two very separate kinds of language. Natural language is how you talk to the AI. Code is how you steer, or even assist, the AI. If the code part needs to change, you either ask the AI to do it or you study what it wrote.

It doesn't have to be that way. Look at Gherkin. It reads like natural English, but each sentence maps to a pre-programmed action that does one specific thing. It was built for testing and for letting people read how a system behaves. Because every phrase is backed by real code, it's deterministic and runs the same way each time:

Scenario: User withdraws cash from ATM
  Given john's balance is $200
  When john withdraws $100 from the ATM
  Then john's balance should be $100

Having a language like that to instruct an AI would be wonderful, but there's no reason to stop at testing syntax. Gherkin serves tests, and tests matter to any business, yet businesses need more than tests. You can have rules written in a form closer to your domain or simpler than code. Business rules, guardrails and living written specifications all qualify, each tailored to your needs. They're readable at a glance, easy to change, and they keep determinism.

Think of it as a third language: not code, not natural language, but deterministic. It might look like this:

rule "refund window":
  when order.status is "delivered"
  and days_since(order.delivered_at) <= 30
  then allow refund
  else deny "refund window expired"

guardrail "no medical advice":
  if response mentions [diagnosis, dosage, prescription]
  then block and escalate to human

The AI would build and maintain the machinery that runs it, and could even help you design the language your problem needs. Once more, though: how often does it offer to?

You're the bottleneck

That brings us back to the thesis. How many of these options does a model suggest on its own? Usually none.

AI has astonishing power and a rich body of theory behind it, and none of it helps if you don't know how to put it to work. It's an odd position. Even if the model knows the theory, you can't use it unless you know it exists.

The good news is that you no longer need all the theory. Knowing what's possible and how it could help is enough. The skill has moved from knowing how to do something to knowing what to ask for. Once you know that, you can draw on everything the model knows.

One prompt away

So the universities' idea of dropping compiler courses is still a bad one. Maybe the depth doesn't need to be what it used to be, but students must know the subject exists. Oddly, AI itself depends on your knowing what's possible.

The same goes for many other fields. A model holds a huge amount of knowledge, much of it buried in its latent space, waiting for the right prompt to wake it up. All of it is one prompt away, provided you know what to prompt for.