Програмування абсолютним двійковим кодом
Перші програмісти писали абсолютним двійковим кодом: кожна інструкція та кожна адреса у формі сирих двійкових цифр. Одна інструкція могла виглядати як 01100101 00001010 — код інструкції та адреса пам'яті в двійковій системі.
Проблема спагетті-коду
Коли помилка потребувала вставлення нової інструкції, програмісти стояли перед дилемою. Вставлення на місце означало, що кожна наступна адреса інструкції змінювалась на одиницю — потребуючи від програміста оновити кожне посилання на адресу в усій програмі. Катастрофічно.
Рішення: замінити інструкцію прямо перед точкою вставлення на перехід до вільної пам'яті. У тій вільній локації: записати перезаписану інструкцію, додати нові інструкції, потім повернутись. Коли помилки з'являлися в коригуваннях, застосувати той же трюк знову, використовуючи іншу вільну пам'ять.
Результат: шлях виконання через програму перестрибував на позірно випадкові локації. Хеммінг називав це 'банкою спагетті'. Шлях потоку керування, намальований на папері, виглядав точно як переплутане спагетті.
Способи рішення
Два негайних поліпшення: восьмирична нотація (групування двійкових цифр наборами по 3) та шістнадцяткова (групи по 4, використання A–F для значень понад 9). Вони скоротили помилки при написанні, але не вирішили фундаментальну проблему адреси.
Символічний асемблер (наприклад, SAP від IBM — Symbolic Assembly Program — та SOAP — Symbolic Optimizing Assembly Program на IBM 650) дозволив програмістам писати назви інструкцій (ADD, MOVE) та символічні мітки адреси замість двійкового коду. Асемблер перекладав у двійковий формат під час введення, автоматично керуючи призначенням адрес.
SOAP здійснив додаткову оптимізацію: він розташував інструкції на барабані, що обертається, таким чином, щоб наступна інструкція прибула до голівки читання саме коли попередня завершилась — кодування з мінімальною затримкою. SOAP навіть компілював сам себе: програму A обробляли як дані для отримання B, B запускали на A для вимірювання, наскільки самокомпіляція це поліпшила.
Бібліотеки та перемовлюваний код
Хеммінг зазначив, що ідея повторно використовуваного програмного забезпечення (математичні бібліотеки) виникла дуже рано — Беббідж їх розумів. Проблема: бібліотека з абсолютною адресою потребувала, щоб кожна підпрограма займала одні й ті ж локації пам'яті кожного разу. Коли загальний розмір бібліотеки виріс занадто, програми конкурували за однакові адреси.
Рішення: перемовлюваний код. Асемблер генерує інструкції, які посилаються на пам'ять відносно — зміщення від базової адреси — а не абсолютні адреси. Лінкер розв'язує фінальні адреси під час завантаження.
Неопубліковані звіти Фон Неймана (широко поширені) описували необхідні прийоми програмування. Перша опублікована книга програмування (Wilkes, Wheeler & Gill, EDSAC, 1951) кодифікувала ці методи.
Розвилка дизайну мови
FORTRAN (1957, IBM) та ALGOL (1958, міжнародний комітет) представляють дві філософії дизайну, які привели до радикально різних результатів.
FORTRAN
Джон Бакус очолив проект FORTRAN (FORmula TRANslation) у IBM. Мета дизайну: зробити мову легкою для вчених та інженерів. FORTRAN прийняла математичну нотацію, яка здавалась природною її користувачам: A = B + C * D замість ADD B, C; STORE T; MULTIPLY T, D; STORE A.
FORTRAN вижила 60+ років. Вона залишається в активному використанні в науковому обчисленні, динаміці рідин, моделюванні клімату та обчислювальній фізиці. Хеммінг відзначив цю довговічність як доказ успішного дизайну.
ALGOL
ALGOL (ALGOrithmic Language) був розроблений комітетом логіків та комп'ютерних науковців, спрямованих на математичну строгість: логічно чиста, формально визначена мова. Форма Бакуса-Наура (BNF) для опису граматик була винайдена для специфікації ALGOL.
ALGOL не вдався на практиці. Незважаючи на його логічну елегантність та його величезний вплив на наступний дизайн мови (Pascal, C та практично кожна сучасна мова походить від концепцій граматики ALGOL), сам ALGOL ніколи не був широко розгорнутий. Вердикт Хеммінга: логічно розроблена, людськи непрацездатна.
Ієрархія мов
Хеммінг описав природну ієрархію від машинного коду через асемблер, мови вищого рівня та, врешті-решт, 'мова, орієнтована на проблему', близька до того, як практики думають про свою проблему. Кожен рівень додає читаність людини за рахунок ефективності машини.
Чотири критерії дизайну мови Хеммінга
Хеммінг дистильював урок FORTRAN проти ALGOL у чотири критерії для успішної мови програмування:
1. Легко вивчати — новачок може швидко стати продуктивним
2. Легко використовувати — звичайні завдання потребують мінімального церемоніалу
3. Легко налагоджувати — помилки створюють змістовні повідомлення про помилки
4. Легко використовувати підпрограми — повторне використання та абстракція не потребують героїчних зусиль
Він додав структурне спостереження: людська мова носить близько 60% надлишковості; письмова мова близько 40%. Мови низької надлишковості (як APL) виробляють елегантні однорядкові, які експерти вважають гарними та новачки вважають непрозорими — та які містять невиявлені помилки, коли одиниця змінює значення.
Імплікація: мова, розроблена для логічної елегантності, оптимізує для неправильного читача. Програміст — людина; люди потребують надлишковості, щоб впіймати помилки та передати намір.
Психологічний проти логічного дизайну мови
Хеммінг повернувся до контрасту FORTRAN/ALGOL як урок інституційної та людської динаміки, а не просто дизайну мови.
FORTRAN було розроблено психологічно — для людей, які б його використовували, конкретно для вчених, які думали математичною нотацією. ALGOL було розроблено логічно — для формальної коректності та теоретичної елегантності.
Парадокс, який виявив Хеммінг: логічно коректна мова, від якої люди чекають, не вдається; прагматично розроблена мова, яку люди адаптують, вдається, навіть якщо вона логічно більш запутана.
Він наводив APL як крайній випадок: логічно елегантна, однорядкова, з її власним спеціальним набором символів. Експерти її любили. Звичайні програмісти вважали її нечитаною. Одна зміна символу може мовчки трансформувати значення програми. APL має невелику присвячену громаду та практично нульове основне використання.
Аргумент людської надлишковості: розмовна мова ~60% надлишкова (повторний контекст, уточнюючі слова, передбачувана структура). Письмова мова ~40% надлишкова. Цю надлишковість служить виявленню помилок — люди ненадійні, тому мова еволюціонувала, щоб нести достатньо повторної інформації для перехоплення та виправлення помилок. Мова низької надлишковості видаляє цю сітку безпеки.
Ієрархія компіляторів
Хеммінг описав шарування компілятора/інтерпретатора: програма може читати мову вищого рівня та перекладати її на нижчу. Складіть ці шари — кожен перекладає один рівень вниз. На вершині: мова, орієнтована на область, яку експерти у галузі (біологія, фінанси, фізика) писали природною. На дні: машинний код. Кожен перехід — це компілятор або інтерпретатор.
Прогнозування виживання мови
До 1993 року Хеммінг спостерігав за тим, як багато мов вдалося та не вдалося. FORTRAN (1957) вижив. ALGOL (1958) не вдався. COBOL (1959) вижив десятиліття в бізнес-обчисленнях. LISP (1958) вижив у дослідженнях штучного інтелекту. PL/I (1964) спробував об'єднати все та не вдався.
Повторювана закономірність
Розділ історії програмного забезпечення Хеммінга містить повторювану структуру:
1. Болісне обмеження існує (абсолютні адреси, двійкова нотація, невідшліфований код)
2. Хтось винаходить шар абстракції, який приховує обмеження
3. Абстракція дозволяє новий масштаб, який створює нові болісні обмеження
4. Повторіть
Двійковий → восьмеричний/шістнадцятковий → символічний асемблер → FORTRAN → структуроване програмування → об'єктно-орієнтовані мови → мови, орієнтовані на область. Кожен шар вирішує біль, виявлений попередником, при цьому вводячи новий клас проблеми.
Лекція Хеммінга закінчилася до цих пізніших переходів, але закономірність продовжується.
Його урок для інженерів: ви завжди вирішуєте біль, виявлену шаром абстракції попередника. Розуміння шару, на якому ви перебуваєте, потребує знання, чому шар нижче існує.