← Wszystkie wpisy

Filozofia inżynierii: Roberto Ierusalimschy

Roberto Ierusalimschy, twórca języka Lua

Najważniejsze wnioski

  • Zaprojektował język skryptowy ukryty wewnątrz ogromnej części oprogramowania, z którym ma się styczność na co dzień. Roberto Ierusalimschy kierował projektem języka Lua, stworzonego w 1993 roku w laboratorium Tecgraf na uczelni PUC-Rio wraz z Luizem Henrique de Figueiredo i Waldemarem Celesem. Obsługuje on logikę gier w World of Warcraft, Roblox (poprzez dialekt Luau), Dota 2 oraz Garry’s Mod, służy do skryptowania Redis i nginx (przez OpenResty), napędza Adobe Lightroom i Wireshark oraz osadza się w urządzeniach aż po roboty, pod którymi nie działa żaden system operacyjny.127
  • Mechanizmy, nie polityka. Cechą definiującą Lua jest odmowa — język nie dostarcza systemu klas, modelu obiektowego ani paradygmatu. Daje za to kilka potężnych, ogólnych mechanizmów (tablice, metatablice, korutyny) i pozwala budować własne abstrakcje na ich bazie. „Lua nie narzuca programiście żadnego modelu obiektów czy klas”, napisali projektanci; zamiast tego „dostarczają elastycznych mechanizmów”, aby można było zbudować dowolny model pasujący do aplikacji.3
  • Niewielki rozmiar to zaleta, nie ograniczenie. Implementacja referencyjna jest maleńka i czysta — kod źródłowy Lua 5.1 liczy około 17 000 wierszy w języku C, cała dystrybucja mieści się na dyskietce, a zbudowany interpreter waży mniej więcej 143 KB. Większość języków skryptowych jest o rząd wielkości większa; Lua pozostaje mały właśnie dlatego, że jest przeznaczony do osadzania, a prostota to coś, co umożliwia małą i szybką implementację.3
  • Przenośność i osadzalność były celami projektowymi od pierwszego dnia. Lua jest napisany w „czystym C” — części wspólnej języków C i C++ — i „kompiluje się od ręki na większości platform”, od Linuksa i Windowsa po urządzenia kieszonkowe i mikrokontrolery. Dobrze zdefiniowane API w C pozwala programowi-gospodarzowi w pełni go kontrolować i rozszerzać, co jest dokładnie powodem, dla którego branża gier przyjęła go niemal jako standard.34

Zasada

„Nie chcieliśmy zmieniać Lua w język obiektowy, ponieważ nie chcieliśmy ustalać dla Lua jednego paradygmatu programowania… Lua nie narzuca programiście żadnego modelu obiektów czy klas. Zamiast tego… postanowiliśmy dostarczyć elastyczne mechanizmy, które pozwoliłyby programiście zbudować dowolny model odpowiedni dla aplikacji.” — Roberto Ierusalimschy, Luiz Henrique de Figueiredo i Waldemar Celes, The Evolution of Lua3

Projektowanie większości języków polega na nawarstwianiu polityki. Zespół decyduje, że obiekty mają działać w taki sposób, że obsługa błędów ma wyglądać tak, że współbieżność oznacza wątki, asynchroniczność albo aktorów — i wpisuje te decyzje w język, tak że każdy napisany w nim program dziedziczy te wybory. Decyzje są zwykle rozsądne. Problem polega na tym, że są to decyzje podjęte raz, dla wszystkich, przez ludzi, którzy nie mogli znać twojej aplikacji. Instynkt Ierusalimschy’ego biegł w przeciwnym kierunku. Zamiast decydować za użytkownika, Lua podaje mu mały zestaw ogólnych mechanizmów i schodzi z drogi, aby mógł zbudować politykę, jakiej rzeczywiście potrzebuje jego problem.3

Najwyraźniejszym przypadkiem jest orientacja obiektowa. We wczesnych latach 90., gdy programowanie obiektowe było u szczytu mody, użytkownicy naciskali na zespół Lua, by dodał klasy i dziedziczenie. Odmówili — nie dlatego, że OOP było błędne, lecz dlatego, że ustalenie jednego modelu było błędne dla języka mającego być osadzanym w skrajnie różnych aplikacjach. Obiekty i klasy „mogłyby zostać zaimplementowane za pomocą tablic, gdyby zaszła taka potrzeba”, rozumowali, ponieważ tablica może przechowywać zarówno dane, jak i metody, a funkcje są wartościami pierwszej kategorii.3 Zatem zamiast systemu obiektowego zbudowali rozszerzalną semantykę — mechanizm pozwalający ingerować w to, jak zachowują się tablice — i pozwolili użytkownikom samodzielnie składać dziedziczenie, wartości domyślne, widoki tylko do odczytu i pełne hierarchie klas. „Rozszerzalna semantyka stała się znakiem rozpoznawczym Lua”, napisali, i „do dziś nie zmienili zdania”.3

Ta odmowa jest możliwa wyłącznie dzięki trzem innym zobowiązaniom, które idą z nią w parze. Prostota — język pozostaje na tyle mały, że mechanizmy są zrozumiałe, a implementacja poprawna. Niewielki rozmiar jako zaleta — cała implementacja referencyjna liczy około 17 000 wierszy czystego C, na tyle mało, że dodanie Lua do aplikacji „jej nie obciąża”.3 Oraz przenośność — pisanie w części wspólnej C i C++ oznacza, że ten sam kod źródłowy kompiluje się wszędzie, od superkomputera po czujnik.3 Dać ludziom kilka ostrych, ogólnych dźwigni zamiast szafki pełnej z góry zaprogramowanych przycisków i zachować całość na tyle małą, by można ją było wszędzie ze sobą nosić. To właśnie jest zasada, a wszystko inne w Lua jest jej konsekwencją.

Kontekst

Roberto Ierusalimschy urodził się 21 maja 1960 roku w Rio de Janeiro, a cała jego kariera pozostała blisko jednej instytucji — Papieskiego Uniwersytetu Katolickiego w Rio de Janeiro (PUC-Rio), gdzie w 1990 roku uzyskał doktorat z informatyki i gdzie jest profesorem zwyczajnym informatyki. Odbył staż podoktorski na University of Waterloo oraz pracował jako profesor wizytujący na Stanfordzie, ale to PUC-Rio jest jego domem, a historia Lua jest nierozerwalnie związana z pewnym tamtejszym laboratorium.5

Tym laboratorium jest Tecgraf, Grupa Technologii Grafiki Komputerowej, założona w 1987 roku, by tworzyć narzędzia programowe dla klientów przemysłowych — jednym z największych z nich była i nadal jest brazylijska firma naftowa Petrobras.3 Pochodzenie Lua jest po części opowieścią o brazylijskiej polityce handlowej. Od 1977 do 1992 roku Brazylia utrzymywała politykę silnych barier handlowych — „rezerwy rynkowej” — na sprzęt i oprogramowanie komputerowe, napędzaną nacjonalistycznym przekonaniem, że kraj może i powinien budować własne. W tym klimacie klienci Tecgrafu „nie mogli sobie pozwolić, ani politycznie, ani finansowo, na kupowanie dostosowanego oprogramowania z zagranicy”. Zakup zagranicznych narzędzi oznaczał „skomplikowany proces biurokratyczny, mający dowieść, że ich potrzeb nie mogą zaspokoić firmy brazylijskie”. Te ograniczenia, w połączeniu z geograficznym odizolowaniem Brazylii od innych ośrodków badawczych, „skłoniły Tecgraf do napisania od zera podstawowych narzędzi, których potrzebował”.3 Lua jest, według własnych słów projektantów, „jedynym językiem stworzonym w kraju rozwijającym się, który osiągnął globalne znaczenie”.3

Przed Lua istniały dwa małe języki, oba zbudowane dla Petrobrasu. DEL — „data-entry language”, język wprowadzania danych — generował graficzne interfejsy, dzięki którym inżynierowie mogli wprowadzać dane wejściowe symulatora, klikając na diagramie zamiast edytować kolumny liczb.3 SOL — „Simple Object Language”, prosty język obiektowy — był językiem konfiguracyjnym dla generatora raportów, ze składnią deklaracji typów inspirowaną BibTeX-em.3 Na początku 1993 roku oba przerosły swój pierwotny zakres; użytkownicy DEL chcieli prawdziwego przepływu sterowania, a SOL potrzebował mocy proceduralnej. W połowie 1993 roku Ierusalimschy, de Figueiredo i Celes doszli do wniosku, że oba można zastąpić jednym, potężniejszym językiem — i tak narodził się zespół Lua.3 Nazwa to mały żart, który przylgnął na stałe. SOL znaczy po portugalsku „słońce”; przyjaciel z Tecgrafu, Carlos Henrique Levy, zaproponował, by następcę nazwać „Lua” — po portugalsku „księżyc”.3 Do lipca 1993 roku Waldemar Celes ukończył pierwszą implementację, zbudowaną jako projekt zaliczeniowy pod opieką Ierusalimschy’ego, zgodnie z zasadą programowania ekstremalnego: „najprostsza rzecz, która mogłaby zadziałać”.3

Dzieło

Tablice i metatablice: jedna struktura danych, jeden mechanizm

Zacznijmy od tablicy, ponieważ jest ona całym językiem w miniaturze. Lua oferuje jeden mechanizm strukturyzowania danych — tablicę, swój termin na tablicę asocjacyjną — i ta jedna struktura wykonuje pracę rekordów, tablic, zbiorów, list, modułów i obiektów.3 „Choć większość języków skryptowych oferuje tablice asocjacyjne”, zauważają projektanci, „w żadnym innym języku nie odgrywają one tak centralnej roli”.3 Rekord to tablica zaindeksowana nazwami pól; tablica (w sensie ciągu) to tablica zaindeksowana liczbami całkowitymi; obiekt to tablica, która przechowuje zarazem dane i metody. Jeden mechanizm, wiele zastosowań — czyli dokładnie zakład „mechanizmy, nie polityka” zastosowany do danych.

Dźwignią, która czyni tablice programowalnymi, jest metatablica. Normalnie odwołanie się do klucza, którego tablica nie posiada, zwraca nil. Można jednak nadać tablicy metatablicę — drugą tablicę opisującą, jak pierwsza powinna zachowywać się w nietypowych sytuacjach. Najważniejszym wpisem jest __index: gdy wyszukiwanie chybia, Lua zagląda do __index metatablicy, które może wskazywać kolejną tablicę do przeszukania. Połączmy je w łańcuch, a otrzymamy dziedziczenie zbudowane z niczego więcej niż tablic i jednego przekierowania. Ten sam mechanizm, pod różnymi metametodami, daje przeciążanie operatorów, wartości domyślne, pełnomocniki tylko do odczytu i leniwie wyliczane pola. Nic z tego nie jest cechą języka w zwykłym sensie — to wszystko jeden mechanizm, używany w różnych postaciach.3

Ten mechanizm nie pojawił się od razu w całości — jego ewolucja jest sama w sobie lekcją powściągliwości. Zaczął się w Lua 2.1 (1995) jako „fallbacki”: punkty zaczepienia, które Lua wywoływał, gdy operacja była stosowana do „niewłaściwego” rodzaju wartości, tak by programista mógł zdecydować, co stanie się dalej.3 Fallback dla indeksowania tablic pozwalał, by userdata i inne wartości „zachowywały się jak tablice”, wspierając „wiele form dziedziczenia (poprzez delegację)”.3 Fallbacki były globalne — jeden punkt zaczepienia na zdarzenie — co utrudniało ich współdzielenie między bibliotekami, więc Lua 3.0 (1997) zastąpił je metodami przypisanymi do typów („tag methods”), a Lua 5.0 (2003) ujednolicił wszystko w metatablice i metametody, w postaci, jakiej Lua używa dziś. (Termin „metatablica” zaproponował Rici Lake w 2001 roku.)3 Każdy krok uogólniał mechanizm, zamiast dokładać nowych funkcji — ten sam instynkt, który sprawiał, że język wciąż odmawiał dostarczenia systemu klas.

Osadzalność, API w C i dlaczego gry przyjęły Lua

Lua w istocie nie jest pomyślany jako język samodzielny. Projektanci nazywają go „rozszerzalnym językiem rozszerzeń”językiem rozszerzeń, ponieważ rozszerza aplikację-gospodarza przez konfigurację i skryptowanie, oraz rozszerzalnym, ponieważ sam z kolei może być rozszerzany funkcjami w C.3 Zawiasem jest dobrze zdefiniowane API w C, które „umożliwia pełną komunikację między kodem Lua a kodem zewnętrznym”, zbudowane wokół czytelnej metafory stosu do przekazywania wartości tam i z powrotem między Lua a gospodarzem. API pozwala Lua współpracować nie tylko z C i C++, ale poprzez nie także z Fortranem, Javą, C# i zupełnie innymi językami.3

Owoce pojawiły się w grach, a historia jest konkretna. W styczniu 1997 roku Bret Mogilefsky — główny programista przygodowej gry Grim Fandango studia LucasArts — powiedział zespołowi Lua, że zastąpił domowy język skryptowy gry językiem Lua i że „ogromna część gry została napisana w Lua”.3 Mogilefsky opowiedział o tym podczas dyskusji panelowej na Game Developers’ Conference, a stamtąd „Lua rozprzestrzenił się pocztą pantoflową wśród twórców gier, stając się zdecydowanie zbywalną umiejętnością w branży gier”.3 Dopasowanie jest strukturalne. Silnik gry to dokładnie taki rodzaj gospodarza, który zyskuje na osadzonej warstwie skryptowej — ciężka praca (renderowanie, fizyka) pozostaje w szybkim C++, podczas gdy logika gry, zadania i zachowania żyją w małych skryptach Lua, które projektanci mogą edytować i przeładowywać bez rekompilacji silnika. Mały, szybki, przenośny i osadzalny — to był dokładnie profil, jakiego potrzebowała gra, a Lua był pod niego zbudowany od pierwszego wydania.34

Korutyny oraz mała, szybka implementacja

Trzecim wielkim mechanizmem jest korutyna — i podobnie jak OOP, jest to świadoma odmowa polityki ze strony zespołu. W drugiej połowie lat 90. użytkownicy naciskali na wielowątkowość, a oczywistym ruchem byłoby przyjęcie standardowego, wywłaszczeniowego modelu wątków ze współdzieloną pamięcią. Zespół Lua odmówił z dwóch powodów: implementacja wątków wywłaszczeniowych w C wymaga prymitywów, „które nie są częścią ANSI C”, co złamałoby przenośność Lua, oraz — co bardziej zasadnicze — „nie wierzyliśmy (i nadal nie wierzymy) w standardowy model wielowątkowości”, w którym „nikt nie jest w stanie napisać poprawnych programów w języku, gdzie »a=a+1« nie jest deterministyczne”.3 Zatem zamiast wątków dostarczyli korutyny w Lua 5.0 (2003): mechanizm współpracującej, niewywłaszczeniowej wielozadaniowości, w którym funkcja może się zawiesić, a później wznowić dokładnie w miejscu, w którym przerwała.3 Korutyny dają ekspresyjność współbieżności — generatory, planiści współpracujący, automaty stanowe — bez niedeterminizmu, który sprawia, że wątki ze współdzieloną pamięcią tak trudno opanować. Znów mechanizm, nie polityka.

Pod tym wszystkim leży implementacja, która jest szybka właśnie dlatego, że jest mała. Maszyna wirtualna Lua była stosowa aż do Lua 4.0; w Lua 5.0 stała się rejestrowa, co dało generatorowi kodu więcej swobody do optymalizacji i zmniejszyło liczbę instrukcji VM, które wykonuje typowy program. Projektanci zauważają, że „maszyna wirtualna Lua 5.0 była pierwszą rejestrową maszyną wirtualną o szerokim zastosowaniu”.3 Pamięcią zarządza przyrostowy odśmiecacz pamięci (wprowadzony w Lua 5.1), który wykonuje swoją pracę w małych krokach, zamiast w długich pauzach typu „zatrzymaj cały świat” — co jest istotne dla pętli czasu rzeczywistego w grze.3 Niezależne testy wydajnościowe plasują Lua wśród najszybszych interpretowanych języków skryptowych, na tyle szybkich, że — według relacji projektantów — „ponad 40% Adobe Lightroom jest napisane w Lua”, czyli około 100 000 wierszy.3

Język programowania Lua

Zasięg Lua: od World of Warcraft po robota bez systemu operacyjnego

Złóżmy to wszystko razem, a Lua okaże się niewidzialną warstwą skryptową ogromnego wycinka oprogramowania. W grach World of Warcraft udostępnia cały swój interfejs językowi Lua, dzięki czemu gracze mogą pisać dodatki; Roblox zbudował Luau, dialekt Lua, jako język, w którym uczą się programować miliony młodych twórców; osadzają go także Dota 2, Crysis i Garry’s Mod.1 Poza grami Redis uruchamia skrypty Lua atomowo wewnątrz bazy danych; nginx, poprzez dystrybucję OpenResty, używa Lua do programowania samego serwera WWW; Adobe Lightroom i Wireshark są w znacznej mierze na nim oparte; Neovim przyjął Lua jako język konfiguracji i wtyczek pierwszej kategorii.1 A ponieważ interpreter jest mały i przenośny, Lua sięga sprzętu, który nie ma prawie nic innego — projektanci opowiadają o Crazy Ivanie, robocie, który wygrał RoboCup, a którego „mózg” uruchamiał Lua „bezpośrednio na procesorze Motorola Coldfire 5206e bez żadnego systemu operacyjnego; innymi słowy, Lua był systemem operacyjnym”.7 Jeden mały język, zbudowany w laboratorium w Rio pod restrykcjami handlowymi, trafił wszędzie — ponieważ niemal o nic nie prosił, a dawał dźwignie, by zbudować całą resztę.

Metoda

Przyjrzyjmy się DEL, SOL, metatablicy, korutynom i trzydziestu latom odmawiania rozdymania języka, a powracają te same ruchy. Metoda Ierusalimschy’ego to mniej slogan, a bardziej zestaw stałych zobowiązań.

Dostarczaj mechanizmy, nie politykę. Definiującym nawykiem jest dostarczenie ogólnej dźwigni i odmowa decydowania, jak należy jej używać. Żadnego wbudowanego systemu klas, żadnego ustalonego modelu współbieżności — tylko tablice, metatablice i korutyny, z których buduje się to, czego potrzebuje aplikacja. Ogólna lekcja sięga daleko poza projektowanie języków: najtrwalsze narzędzia to te, które dają możliwości, nie dyktując kształtu rozwiązania. To przeciwieństwo „jednego oczywistego sposobu” Guido van Rossuma — i właśnie ten kontrast jest sednem: oba podejścia są spójne, a wiedza o tym, którą filozofię powinno ucieleśniać twoje narzędzie, jest właściwą decyzją.3

Zachowaj to na tyle małym, by było poprawne i dało się wszędzie zabrać. Implementacja referencyjna Lua liczy około 17 000 wierszy czystego C, o rząd wielkości mniej niż inne języki skryptowe, a ten niewielki rozmiar jest celową zaletą — to on czyni język osadzalnym, przenośnym i godnym zaufania. Dyscyplina polega na cięciu, dopóki usunięcie jeszcze jednej rzeczy go nie zepsuje, co czyni Lua programowym odbiciem bezwzględnego minimalizmu Sophie Wilson w ARM-ie — obetnij do nośnej esencji, a wydajność wyjdzie za darmo. To minimalny godny produkt na poziomie środowiska wykonawczego języka.3

Opieraj się funkcji, o którą Cię proszą, dopóki nie znajdziesz mechanizmu, który pod nią leży. Gdy użytkownicy domagali się obiektów, Lua dał im rozszerzalną semantykę; gdy domagali się wątków, dał im korutyny. Za każdym razem zespół odrzucał konkretną politykę i dostarczał ogólniejszy mechanizm, który pozwalał użytkownikom samodzielnie zbudować politykę — i, co kluczowe, nie zmieniał zdania lata później.3 To bramka dowodowa zastosowana do próśb o funkcje: nie dodawaj czegoś dlatego, że ktoś o to prosi; ustal, jaki problem to rozwiązuje i czy mechanizm, który już masz, może go rozwiązać.

Uogólniaj, nie nawarstwiaj. Metatablica nie pojawiła się od razu w całości — wyrosła z fallbacków przez metody typów aż po metametody, a każdy krok zastępował poprzedni mechanizm ogólniejszym, zamiast doczepiać nową funkcję obok.3 Lekcja brzmi: rozwijaj się przez ujednolicanie, nie przez dodawanie, aby język stawał się bardziej zdolny, pozostając tego samego rozmiaru — ta sama ekonomia środków, którą Thompson i Ritchie zastosowali, budując Uniksa z małych, składalnych części.

Pozwól, by ograniczenie zaprojektowało narzędzie. Lua istnieje, ponieważ brazylijskie bariery handlowe zmusiły Tecgraf do zbudowania własnego oprogramowania, a jego przenośność istnieje, ponieważ klienci laboratorium korzystali z „bardzo różnorodnej kolekcji platform komputerowych”.3 Zamiast traktować te ograniczenia jak utrudnienia, Ierusalimschy pozwolił im skierować projekt ku czemuś mniejszemu, czystszemu i bardziej przenośnemu, niż prawdopodobnie zbudowałby dobrze wyposażony zespół — to lekcja, że niedostatek, potraktowany poważnie, jest narzędziem projektowym, a nie wymówką.3

Łańcuch wpływów

Kto go ukształtował

Scheme i tradycja Lispa. Projektanci wprost piszą, że „wpływ Scheme na Lua stopniowo rósł”, stając się „źródłem inspiracji, szczególnie przy wprowadzeniu funkcji anonimowych i pełnego zakresu leksykalnego”.3 Podobnie jak Scheme, Lua jest typowany dynamicznie, traktuje funkcje jak zwyczajne wartości pierwszej kategorii i zachowuje mały rdzeń — przekonanie, że język może być potężny przez bycie minimalnym, a nie wielkim. (Wpływ formacyjny)

DEL i SOL, dwa małe języki. Lua nie wyrósł z teorii; wyrósł z dwóch języków dziedzinowych zbudowanych dla Petrobrasu. SOL wniósł składnię konstruktora tablic do opisywania danych, a doświadczenie z obydwoma nauczyło zespół, że „duże części złożonych aplikacji można napisać za pomocą osadzalnych języków skryptowych” — to uświadomienie, na którym, jak sami mówią, opierały się narodziny Lua.3 (Wpływ bezpośredni)

Brazylijska rezerwa rynkowa. Bariery handlowe obowiązujące od 1977 do 1992 roku to nieoczekiwany, ale prawdziwy wpływ: to z ich powodu Tecgraf w ogóle budował własne narzędzia, a różnorodność platform jego klientów jest powodem, dla którego przenośność stała się celem projektowym pierwszego rzędu. To polityka stworzyła język. (Wpływ formacyjny)

Kogo on ukształtował

Warstwa skryptowa branży gier. Począwszy od Grim Fandango Lua stał się niemal domyślnym wyborem do osadzania logiki gry, a wzorzec, który spopularyzował — szybki silnik w C++, edytowalne zachowanie w osadzonym skrypcie — jest dziś standardową praktyką. Luau od Roblox nauczył całe pokolenie dzieci programować w dialekcie Lua. (Wpływ definiujący dziedzinę)

Oprogramowanie wbudowane i systemowe. Redis, nginx/OpenResty, Wireshark, Neovim i niezliczone urządzenia przyjęły Lua jako warstwę skryptową właśnie dlatego, że tak niewiele wymaga. Niewiele języków sięga od bazy danych przez analizator sieciowy aż po robota działającego na gołym sprzęcie.

Projektanci języków, którzy cenią niewielki rozmiar. Lua stoi jako roboczy dowód na to, że maleńki, niezależny od paradygmatu rdzeń może wygrać z wielkimi, narzucającymi opinie językami w niszy osadzania — argument, który pobrzmiewa w każdym projekcie minimalnego środowiska wykonawczego i każdym „mechanizmie ponad framework” od tamtej pory.

Nić przewodnia

Ierusalimschy stoi na jednym biegunie długiego sporu tej serii o to, jak wiele narzędzie powinno decydować za Ciebie. Guido van Rossum zbudował Python na zasadzie „powinien istnieć jeden oczywisty sposób, by to zrobić” i dostarczył „baterie w komplecie” — język ma opinie, a podążanie za nimi jest drogą najmniejszego oporu. Bjarne Stroustrup zbudował C++ na tyle bogatym, by wyrazić niemal dowolny paradygmat, płacąc za to bogactwo rozmiarem i złożonością. Ierusalimschy poszedł w przeciwną stronę niż obaj: dostarczyć niemal żadnej polityki, niemal żadnych funkcji, tylko kilka ogólnych mechanizmów i 17-tysięczne środowisko wykonawcze, a resztę pozostawić użytkownikowi do zbudowania. Jest programowym odbiciem Sophie Wilson, której ARM wygrał, będąc obciętym do mniej niż 25 000 tranzystorów — Lua jest tą samą bezwzględną małością w C, a podobnie jak przypadkowo niski pobór mocy ARM-a, jego niewielki rozmiar okazał się właśnie tym, co pozwoliło mu podbić dziedzinę (osadzanie), do której nikt go nie zaprojektował. Tam gdzie van Rossum mówi oto oczywisty sposób, a Stroustrup mówi oto każdy sposób, Ierusalimschy mówi: oto dźwignie — Ty decydujesz, co zbudować, a ja zachowam całość na tyle małą, by można ją było wszędzie zabrać. (Pomost serii)

Co z tego biorę

Lekcja, którą zachowuję od Ierusalimschy’ego, brzmi: najsilniejszą rzeczą, jaką można komuś podać, jest mechanizm, a nie polityka. Kusi, by budując narzędzie, wpisać w nie własną opinię o tym, jak należy go używać — dostarczyć system klas, framework, jeden błogosławiony przepływ pracy. Cała kariera Lua to argument, że odwrotne podejście starzeje się lepiej. Odmawiając ustalenia modelu obiektów i dostarczając zamiast niego metatablicę, język pozwolił użytkownikom zbudować schematy dziedziczenia, jakich jego projektanci nigdy sobie nie wyobrażali, w grach, bazach danych i edytorach, których nigdy nie przewidzieli. Gdy buduję teraz coś nowego, pytanie, które zapożyczam od Lua, brzmi: czy dostarczam decyzję, czy możliwość — ponieważ decyzja służy przypadkom, które widzę dzisiaj, a możliwość służy tym, których nie widzę.

Druga lekcja brzmi: niewielki rozmiar to nie ograniczenie, które się toleruje — to zaleta, której się broni. Łatwo byłoby, przez trzydzieści lat presji użytkowników, pozwolić Lua rozrosnąć się w wielki język ze słowem kluczowym class, biblioteką wątków i standardowym frameworkiem do wszystkiego. Zespół odmówił, a ta odmowa jest powodem, dla którego Lua mieści się na dyskietce, kompiluje się wszędzie i osadza się w działającym na gołym sprzęcie mózgu robota. Dyscyplina pozostawania małym — uogólniania zamiast nawarstwiania, odmawiania funkcji, dopóki nie znajdzie się leżącego pod nią mechanizmu — jest trudniejsza niż dyscyplina dodawania i znacznie cenniejsza. To jakość jest jedyną zmienną odczytane jako powściągliwość: właściwa mała rzecz, broniona przez dekady, staje się infrastrukturą, na której może stanąć wszystko inne.

FAQ

Kim jest Roberto Ierusalimschy i co stworzył?

Roberto Ierusalimschy jest brazylijskim informatykiem, urodzonym w 1960 roku w Rio de Janeiro i profesorem zwyczajnym informatyki na PUC-Rio. Jest głównym projektantem języka Lua, małego, szybkiego, osadzalnego języka skryptowego, który stworzył w 1993 roku w laboratorium Tecgraf na PUC-Rio wraz z Luizem Henrique de Figueiredo i Waldemarem Celesem. Jest także autorem książki Programming in Lua oraz twórcą LPeg, biblioteki dopasowywania wzorców, a ponadto współautorem podstawowej historii języka, „The Evolution of Lua”.356

Co oznacza „mechanizmy, nie polityka” w Lua?

Oznacza, że Lua daje kilka potężnych, ogólnych mechanizmów, zamiast narzucać ustalone decyzje. Najwyraźniejszym przykładem jest orientacja obiektowa: Lua nie ma wbudowanego systemu klas. Zamiast tego oferuje tablice (które mogą przechowywać zarówno dane, jak i metody) oraz metatablice (które pozwalają dostosować to, jak tablica się zachowuje), a dziedziczenie, klasy czy dowolny inny model obiektowy buduje się samodzielnie. Projektanci napisali, że „Lua nie narzuca programiście żadnego modelu obiektów czy klas”, i wybrali „dostarczenie elastycznych mechanizmów” — stanowisko, które utrzymują od 1993 roku.3

Dlaczego Lua jest tak powszechnie używany w grach i systemach wbudowanych?

Ponieważ od początku zaprojektowano go tak, by był mały, szybki, przenośny i łatwy do osadzenia. Implementacja referencyjna liczy około 17 000 wierszy czystego ANSI C, które kompilują się niemal wszędzie, zbudowany interpreter waży mniej więcej 143 KB, a dobrze zdefiniowane API w C pozwala programowi-gospodarzowi w pełni go kontrolować. To dokładnie taki profil, jakiego potrzebuje silnik gry lub urządzenie wbudowane — ciężka praca w C/C++, edytowalna logika w małych skryptach — i dlatego skryptuje World of Warcraft, Roblox (poprzez Luau), Redis, nginx/OpenResty, Adobe Lightroom oraz urządzenia aż po roboty na gołym sprzęcie.137

Skąd wzięła się nazwa „Lua”?

Lua znaczy po portugalsku „księżyc”. Nazwano go jako następcę wcześniejszego języka Tecgrafu o nazwie SOL — „Simple Object Language”, a zarazem „słońce” po portugalsku. Kolega z Tecgrafu, Carlos Henrique Levy, zaproponował dla nowego języka nazwę „Lua”, dopełniając przejście od słońca do księżyca. Cały projekt wyrósł z dwóch małych języków, DEL i SOL, zbudowanych dla brazylijskiej firmy naftowej Petrobras.3


Źródła


  1. “Lua (programming language),” Wikipedia. Lua został stworzony w 1993 roku przez Roberta Ierusalimschy’ego, Luiza Henrique de Figueiredo i Waldemara Celesa w Grupie Technologii Grafiki Komputerowej (Tecgraf) na Papieskim Uniwersytecie Katolickim w Rio de Janeiro (PUC-Rio) w Brazylii; zaimplementowany w ANSI C, na licencji MIT, z rejestrową maszyną wirtualną od Lua 5.0, odśmiecaniem pamięci, korutynami i funkcjami pierwszej kategorii. Najnowsze implementacje referencyjne pozostają małe (artykuł odnotowuje, że Lua 5.5.0 obejmuje około 32 000 wierszy C). Udokumentowani użytkownicy obejmują gry World of Warcraft, Roblox (który opracował Luau, pochodną Lua), Dota 2, Crysis i Garry’s Mod oraz oprogramowanie niegrowe, w tym Redis, nginx (poprzez OpenResty), Adobe Lightroom, Wireshark i Neovim. 

  2. “Roberto Ierusalimschy: Known for Lua Programming Language,” machaddr (profil biograficzny). Podsumowuje rolę Ierusalimschy’ego jako twórcy i głównego architekta Lua, opracowanego wraz z Luizem Henrique de Figueiredo i Waldemarem Celesem na PUC-Rio, wydanego po raz pierwszy w 1993 roku; odnotowuje światowe wykorzystanie Lua w oprogramowaniu, w tym World of Warcraft i Adobe Lightroom, jego autorstwo Programming in Lua oraz stworzenie biblioteki LPeg. 

  3. Roberto Ierusalimschy, Luiz Henrique de Figueiredo i Waldemar Celes, “The Evolution of Lua,” Proceedings of the Third ACM SIGPLAN Conference on the History of Programming Languages (HOPL III), 2007. Źródło pierwotne. „Lua jest językiem skryptowym narodzonym w 1993 roku na PUC-Rio.” „Od początku Lua był projektowany tak, by być prosty, mały, przenośny, szybki i łatwy do osadzenia w aplikacjach.” Jedna struktura danych: „oferuje jeden rodzaj struktury danych, tablicę.” Zaimplementowany w „czystym C”, „części wspólnej C i C++”, i „kompiluje się od ręki na większości platform”. Mały rozmiar: kod źródłowy Lua 5.1 liczy „około 17 000 wierszy C”, archiwum „mieści się wygodnie na dyskietce”, a „interpreter Lua zbudowany ze wszystkimi standardowymi bibliotekami Lua zajmuje 143K”. „ponad 40% Adobe Lightroom jest napisane w Lua (co stanowi około 100 000 wierszy kodu Lua).” O brazylijskiej polityce handlowej: „Od 1977 do 1992 roku Brazylia prowadziła politykę silnych barier handlowych (zwaną »rezerwą rynkową«) dla sprzętu i oprogramowania komputerowego”, i „te powody skłoniły Tecgraf do napisania od zera podstawowych narzędzi, których potrzebował”; Lua jest „jedynym językiem stworzonym w kraju rozwijającym się, który osiągnął globalne znaczenie”. Poprzednicy DEL („data-entry language”) i SOL („Simple Object Language”). Nazewnictwo: SOL znaczy po portugalsku słońce, a „przyjaciel z Tecgrafu (Carlos Henrique Levy) zaproponował nazwę »Lua« (księżyc po portugalsku) i tak narodził się Lua”. Pierwsza implementacja ukończona w lipcu 1993 roku zgodnie z zasadą „najprostszej rzeczy, która mogłaby zadziałać”. O zasadzie „mechanizmy, nie polityka”: „nie chcieliśmy zmieniać Lua w język obiektowy, ponieważ nie chcieliśmy ustalać dla Lua jednego paradygmatu programowania”; „Lua nie narzuca programiście żadnego modelu obiektów czy klas”; „postanowiliśmy dostarczyć elastyczne mechanizmy, które pozwoliłyby programiście zbudować dowolny model odpowiedni dla aplikacji”; „Rozszerzalna semantyka stała się znakiem rozpoznawczym Lua”. Ewolucja mechanizmu: fallbacki (Lua 2.1, 1995) → metody typów (Lua 3.0, 1997) → metatablice i metametody (Lua 5.0, 2003); fallbacki dla indeksowania tablic wspierały „wiele form dziedziczenia (poprzez delegację)”; termin „metatablica” zaproponował Rici Lake w 2001 roku. Lua jako „rozszerzalny język rozszerzeń” z API w C umożliwiającym „pełną komunikację między kodem Lua a kodem zewnętrznym”. Gry: w styczniu 1997 roku Bret Mogilefsky, główny programista Grim Fandango studia LucasArts, doniósł, że „ogromna część gry została napisana w Lua”, po czym „Lua rozprzestrzenił się pocztą pantoflową wśród twórców gier, stając się zdecydowanie zbywalną umiejętnością w branży gier”. Współbieżność: implementacja wątków wywłaszczeniowych wymaga prymitywów „nienależących do ANSI C”, a „nie wierzyliśmy (i nadal nie wierzymy) w standardowy model wielowątkowości… nikt nie jest w stanie napisać poprawnych programów w języku, gdzie »a=a+1« nie jest deterministyczne”; współpracująca wielozadaniowość poprzez korutyny została dostarczona w Lua 5.0 (2003). VM: „Od Lua 5.0 maszyna wirtualna jest rejestrowa”, a „maszyna wirtualna Lua 5.0 była pierwszą rejestrową maszyną wirtualną o szerokim zastosowaniu”; przyrostowe odśmiecanie pamięci pojawia się w Lua 5.1. 

  4. “Lua: about,” Lua.org (oficjalna strona). „Lua jest potężnym, wydajnym, lekkim, osadzalnym językiem skryptowym.” Jest „zaimplementowany jako mała biblioteka funkcji C, napisana w czystym C, części wspólnej Standard C i C++”, i „działa na wszelkiego rodzaju systemach Unix i Windows, a także na urządzeniach mobilnych… mikroprocesorach wbudowanych… oraz komputerach mainframe IBM”. Opisuje Lua jako łączący „prostą składnię proceduralną z potężnymi konstrukcjami opisu danych opartymi na tablicach asocjacyjnych i rozszerzalnej semantyce”, z typowaniem dynamicznym, automatycznym zarządzaniem pamięcią z przyrostowym odśmiecaniem oraz jako „szybki” i „przenośny”. 

  5. “Roberto Ierusalimschy,” Wikipedia, potwierdzone przez jego “profil na wydziale,” Wydział Informatyki, PUC-Rio. Urodzony 21 maja 1960 roku w Rio de Janeiro w Brazylii; doktorat z informatyki na PUC-Rio (1990); profesor zwyczajny informatyki na PUC-Rio; praca podoktorska na University of Waterloo oraz profesura wizytująca na Stanfordzie (2012). Twórca i główny architekt języka programowania Lua, autor Programming in Lua (cztery wydania) oraz twórca LPeg, biblioteki Lua do implementowania gramatyk wyrażeń parsujących. 

  6. “Programming in Lua,” autorstwa Roberta Ierusalimschy’ego, Lua.org. Oficjalna książka o Lua, napisana przez jego głównego projektanta, opisująca projekt i zastosowanie języka; pierwsze wydanie jest swobodnie dostępne online. Wzmacnia projekt Lua oparty na tablicach jako jedynym mechanizmie strukturyzowania danych, metatablicach dla rozszerzalnego zachowania, funkcjach pierwszej kategorii oraz korutynach dla współpracującej wielozadaniowości. 

  7. Roberto Ierusalimschy, Luiz Henrique de Figueiredo i Waldemar Celes, “The Evolution of Lua,” HOPL III, 2007, przypis o zastosowaniu wbudowanym. „Crazy Ivan, robot, który wygrał RoboCup w 2000 i 2001 roku w Danii, miał »mózg« zaimplementowany w Lua. Lua działał bezpośrednio na procesorze Motorola Coldfire 5206e bez żadnego systemu operacyjnego (innymi słowy, Lua był systemem operacyjnym). Lua był przechowywany w pamięci ROM systemu i wczytywał programy przy starcie z portu szeregowego.” Ilustruje zasięg Lua w urządzeniach wbudowanych na gołym sprzęcie, będący konsekwencją jego małej, przenośnej implementacji. 

Powiązane artykuły

Filozofia inżynierska: Guido van Rossum, czytelność się liczy

Guido van Rossum zbudował Pythona na jednym założeniu: kod jest czytany znacznie częściej, niż jest pisany, więc sam jęz…

14 min czytania

Filozofia inżynierska: Grace Hopper i nakaz, by to komputer mówił po ludzku

Grace Hopper zbudowała pierwszy kompilator, aby programy można było pisać w ludzkim języku, uczyniła opóźnienie namacaln…

15 min czytania

The Shader Gallery That Lied: Debugging 216 WebGL Presets

A user said the shader playground looked broken. Pixel-readback testing found 30 dead presets, 11 that never compiled, a…

11 min czytania