Головна / Статті / Чому теорія компіляторів досі важлива, коли код за вас пише ШІ

Чому теорія компіляторів досі важлива, коли код за вас пише ШІ

ШІ згенерує парсер за хвилини, але не скаже, коли він вам потрібен. Як маленькі мови й детерміновані правила перемагають промпти і чому вузьке місце — це ви.

1253 слів

ШІ на запит напише компілятор за кілька хвилин. То теорія компіляторів як предмет більше не потрібна? Зовсім ні. Побудувати компілятор ніколи не було найскладнішим. Складно помітити, що він вам потрібен, а модель вам про це не скаже.

Стара суперечка з новим виправданням

Університети вже давно скорочують курси з компіляторів у своїх програмах. Аргумент проти них звучить розумно: написання компілятора — вузьке знання, потрібне лише на небагатьох посадах, тож більшість програмістів за всю кар'єру жодного не напише.

Контраргумент такий: написати компілятор C чи JavaScript — одне, а все, що дає змогу робити теорія компіляторів, — зовсім інше. Вважати її корисною лише для створення мов програмування означає недооцінювати її. Ті самі ідеї лежать в основі серйозної обробки тексту, а ще роблять код простішим і зрозумілішим.

Тепер ШІ дав скептикам нові аргументи. Модель знає всю теорію й сама згенерує код парсингу, то навіщо щось вчити? Це справжній аргумент чи просто відмовка?

Застосування, про які ніхто не питає

Візьмімо невелике навчальне завдання з калькулятором, де тести дані заздалегідь, а студент реалізує поведінку. Випадків десятки, і кожен тест виглядає приблизно так:

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

Окремо читається нормально, але стежити за цим втомливо. Помножте на сорок, і ви перестанете розуміти, що покрито, а що ні. Трохи «компіляторного» мислення, і можна написати один хелпер, який грає роль користувача й усе перевіряє, тож тест стискається до одного рядка:

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

Читати це набагато простіше. І можна піти далі. Замість сорока блоків тестів можна написати таблицю, де кожен рядок — окремий випадок, очікуване показання дисплея в дужках і коментар про те, що цей випадок доводить:

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);

Частину випадків тут пропущено, але різницю видно. Уявіть, що вам треба зробити рев'ю такого самого покриття, розписаного випадок за випадком.

Ще одне місце, де це окупається, — зв'язування залежностей між модулями. У коді ви написали б щось на кшталт цього:

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');

Трохи проєктування мови, і натомість ви напишете ось що:

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

З першим варіантом часто потрібні олівець і папір, щоб відтворити граф у голові. У другому ви просто бачите граф. Реалізація майже непристойно проста: розбити на рядки, а кожен рядок розбити за стрілкою.

Прикладів ще багато, і іноді компілятор навіть писати не треба, бо він уже існує. Багато конфігурацій цілком можна тримати в YAML. Система сутностей і компонентів, поширена в іграх, легко росте й адаптується, коли сутності описані так:

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

Перш ніж іти далі, запитайте себе, скільки з цих ідей модель пропонує сама, коли ви просите в неї код. На практиці майже жодної.

ШІ робить це ще потрібнішим

Можна заперечити, що ШІ — це компілятор у найвищій формі: він розуміє майже будь-яку мову й уміє її інтерпретувати. Є одна заковика. Він не завжди робить це правильно. Це одна з головних проблем для всіх, хто автоматизує рутину за допомогою ШІ, бо результат недетермінований. Сьогодні він чудово справляється, а завтра влаштовує безлад.

Зрештою ви працюєте між двома зовсім різними видами мови. Природна мова — це те, як ви розмовляєте з ШІ. Код — те, чим ви ШІ керуєте або навіть допомагаєте йому. Якщо код треба змінити, ви або просите ШІ зробити це, або розбираєтеся в тому, що він написав.

Так бути не мусить. Погляньте на Gherkin. Він читається як звичайна англійська, але кожне речення зіставлене із заздалегідь запрограмованою дією, яка робить одну конкретну річ. Його створювали для тестування й для того, щоб люди могли читати, як поводиться система. Оскільки за кожною фразою стоїть справжній код, він детермінований і щоразу виконується однаково:

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

Мати таку мову для інструкцій ШІ було б чудово, але немає причин обмежуватися синтаксисом для тестів. Gherkin обслуговує тести, а тести важливі для будь-якого бізнесу, проте бізнесу потрібно не лише це. Правила можна записувати у формі, ближчій до вашої предметної області або простішій за код. Бізнес-правила, обмеження й живі письмові специфікації — усе це підходить, і кожне можна підлаштувати під ваші потреби. Вони читаються з одного погляду, легко змінюються й зберігають детермінованість.

Вважайте це третьою мовою: не код і не природна мова, але детермінована. Вона може виглядати так:

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

ШІ побудував би й підтримував механізм, який це виконує, і навіть міг би допомогти вам спроєктувати мову під ваше завдання. Але знову те саме питання: як часто він сам це пропонує?

Вузьке місце — ви

Це повертає нас до головної тези. Скільки з цих варіантів модель пропонує з власної ініціативи? Зазвичай жодного.

ШІ має вражаючі можливості й багату теоретичну базу, і ніщо з цього не допомагає, якщо ви не знаєте, як це застосувати. Дивна ситуація. Навіть якщо модель знає теорію, ви не зможете нею скористатися, доки не знаєте про її існування.

Добра новина в тому, що вся теорія вам більше не потрібна. Досить знати, що можливо і чим це може допомогти. Навичка змістилася від уміння щось зробити до вміння зрозуміти, що попросити. Коли ви це знаєте, вам доступне все, що знає модель.

На відстані одного промпта

Тож ідея університетів відмовитися від курсів із компіляторів і далі погана. Можливо, глибина вже не мусить бути такою, як раніше, але студенти мають знати, що такий предмет існує. Хоч як дивно, сам ШІ залежить від того, чи знаєте ви, що можливо.

Те саме стосується багатьох інших галузей. Модель зберігає величезний обсяг знань, більшість яких захована в її латентному просторі й чекає правильного промпта, щоб прокинутися. Усе це на відстані одного промпта, за умови, що ви знаєте, про що просити.