Bycie popularnym

Maj 2001

(Ten artykuł został napisany jako rodzaj planu biznesowego dla nowego języka. Brakuje mu zatem (ponieważ przyjmuje za pewnik) najważniejszej cechy dobrego języka programowania: bardzo potężnych abstrakcji.)

Mój przyjaciel powiedział kiedyś wybitnemu ekspertowi od systemów operacyjnych, że chce zaprojektować naprawdę dobry język programowania. Ekspert powiedział mu, że to strata czasu, że języki programowania nie stają się popularne ani niepopularne na podstawie swoich zalet, więc bez względu na to, jak dobry byłby jego język, nikt by go nie używał. Przynajmniej tak stało się z językiem, który on zaprojektował.

Co sprawia, że język staje się popularny? Czy popularne języki zasługują na swoją popularność? Czy warto próbować zdefiniować dobry język programowania? Jak byś to zrobił?

Myślę, że odpowiedzi na te pytania można znaleźć, przyglądając się hakerom i ucząc się, czego chcą. Języki programowania są dla hakerów, a język programowania jest dobry jako język programowania (a nie, na przykład, ćwiczenie z semantyki denotacyjnej lub projektowania kompilatorów), jeśli i tylko jeśli hakerzy go lubią.

1 Mechanika popularności

To prawda, że większość ludzi nie wybiera języków programowania po prostu na podstawie ich zalet. Większość programistów jest informowana, jakiego języka użyć przez kogoś innego. A jednak myślę, że wpływ takich czynników zewnętrznych na popularność języków programowania nie jest tak duży, jak czasami się uważa. Myślę, że większym problemem jest to, że wyobrażenie hakera o dobrym języku programowania nie jest takie samo jak wyobrażenie większości projektantów języków.

Między tymi dwoma, opinia hakera ma znaczenie. Języki programowania nie są twierdzeniami. Są narzędziami, zaprojektowanymi dla ludzi, i muszą być zaprojektowane tak, aby pasowały do ludzkich mocnych i słabych stron, tak jak buty muszą być zaprojektowane dla ludzkich stóp. Jeśli but uciska, gdy go zakładasz, to jest to zły but, bez względu na to, jak elegancki jest jako dzieło rzeźbiarskie.

Może się zdarzyć, że większość programistów nie potrafi odróżnić dobrego języka od złego. Ale tak jest z każdym innym narzędziem. Nie oznacza to, że próba projektowania dobrego języka jest stratą czasu. Eksperci hakerzy potrafią rozpoznać dobry język, gdy go zobaczą, i będą go używać. Eksperci hakerzy to oczywiście niewielka mniejszość, ale ta niewielka mniejszość pisze wszystkie dobre oprogramowania, a ich wpływ jest taki, że reszta programistów będzie skłonna używać każdego języka, którego oni używają. Często, co więcej, jest to nie tylko wpływ, ale i rozkaz: często eksperci hakerzy to właśnie ci ludzie, którzy jako ich szefowie lub doradcy akademiccy mówią innym programistom, jakiego języka użyć.

Opinia ekspertów hakerów nie jest jedyną siłą determinującą względną popularność języków programowania — oprogramowanie dziedziczone (Cobol) i szum medialny (Ada, Java) również odgrywają rolę — ale myślę, że jest to najpotężniejsza siła w dłuższej perspektywie. Biorąc pod uwagę początkową masę krytyczną i wystarczająco dużo czasu, język programowania prawdopodobnie staje się tak popularny, jak na to zasługuje. A popularność dalej oddziela dobre języki od złych, ponieważ informacje zwrotne od prawdziwych użytkowników zawsze prowadzą do ulepszeń. Spójrz, jak bardzo zmienił się każdy popularny język w ciągu swojego życia. Perl i Fortran to skrajne przypadki, ale nawet Lisp zmienił się bardzo. Lisp 1.5 nie miał makr, na przykład; ewoluowały one później, po tym jak hakerzy z MIT spędzili kilka lat używając Lisp do pisania prawdziwych programów. [1]

Więc niezależnie od tego, czy język musi być dobry, aby być popularnym, myślę, że język musi być popularny, aby być dobry. I musi pozostać popularny, aby pozostać dobry. Stan techniki w językach programowania nie stoi w miejscu. A jednak Lispy, które mamy dzisiaj, są wciąż takie same jak te z MIT w połowie lat 80., ponieważ wtedy Lisp miał wystarczająco dużą i wymagającą bazę użytkowników.

Oczywiście, hakerzy muszą wiedzieć o języku, zanim będą mogli go użyć. Jak mają się o nim dowiedzieć? Od innych hakerów. Ale musi istnieć jakaś początkowa grupa hakerów używających języka, aby inni mogli się o nim dowiedzieć. Zastanawiam się, jak duża musi być ta grupa; ilu użytkowników stanowi masę krytyczną? Z pamięci, powiedziałbym dwudziestu. Gdyby język miał dwudziestu niezależnych użytkowników, czyli dwudziestu użytkowników, którzy sami zdecydowali się go użyć, uznałbym go za prawdziwy.

Dotarcie tam nie może być łatwe. Nie zdziwiłbym się, gdyby trudniej było przejść od zera do dwudziestu niż od dwudziestu do tysiąca. Najlepszym sposobem na zdobycie tych początkowych dwudziestu użytkowników jest prawdopodobnie użycie konia trojańskiego: danie ludziom aplikacji, której chcą, która przypadkiem jest napisana w nowym języku.

2 Czynniki zewnętrzne

Zacznijmy od uznania jednego czynnika zewnętrznego, który wpływa na popularność języka programowania. Aby stać się popularnym, język programowania musi być językiem skryptowym popularnego systemu. Fortran i Cobol były językami skryptowymi wczesnych mainframe'ów IBM. C był językiem skryptowym Unix, a później także Perl. Tcl jest językiem skryptowym Tk. Java i Javascript są przeznaczone do bycia językami skryptowymi przeglądarek internetowych.

Lisp nie jest językiem masowo popularnym, ponieważ nie jest językiem skryptowym masowo popularnego systemu. Popularność, którą zachowuje, sięga lat 60. i 70., kiedy był językiem skryptowym MIT. Wielu wybitnych programistów tamtych czasów było związanych z MIT w pewnym momencie. A na początku lat 70., przed C, dialekt Lisp z MIT, zwany MacLisp, był jednym z niewielu języków programowania, których chciałby użyć poważny haker.

Dziś Lisp jest językiem skryptowym dwóch umiarkowanie popularnych systemów, Emacs i Autocad, i z tego powodu podejrzewam, że większość programowania w Lisp odbywa się dziś w Emacs Lisp lub AutoLisp.

Języki programowania nie istnieją w izolacji. Hacking to czasownik przechodni — hakerzy zazwyczaj coś hakują — i w praktyce języki są oceniane w stosunku do tego, do czego są używane do hakowania. Więc jeśli chcesz zaprojektować popularny język, musisz albo dostarczyć więcej niż tylko język, albo zaprojektować swój język tak, aby zastąpić język skryptowy istniejącego systemu.

Common Lisp jest niepopularny częściowo dlatego, że jest sierotą. Pierwotnie posiadał system do hakowania: Lisp Machine. Ale Lisp Machines (wraz z komputerami równoległymi) zostały zmiażdżone przez rosnącą moc procesorów ogólnego przeznaczenia w latach 80. Common Lisp mógłby pozostać popularny, gdyby był dobrym językiem skryptowym dla Uniksa. Jest, niestety, żałośnie zły.

Jednym ze sposobów opisania tej sytuacji jest stwierdzenie, że język nie jest oceniany na podstawie własnych zalet. Inną perspektywą jest to, że język programowania tak naprawdę nie jest językiem programowania, chyba że jest również językiem skryptowym czegoś. To wydaje się niesprawiedliwe tylko wtedy, gdy jest to niespodzianka. Myślę, że to nie jest bardziej niesprawiedliwe niż oczekiwanie, że język programowania będzie miał, powiedzmy, implementację. To po prostu część tego, czym jest język programowania.

Język programowania potrzebuje dobrej implementacji, oczywiście, i musi być darmowy. Firmy zapłacą za oprogramowanie, ale indywidualni hakerzy nie, a to właśnie hakerów trzeba przyciągnąć.

Język potrzebuje również książki na swój temat. Książka powinna być cienka, dobrze napisana i pełna dobrych przykładów. K&R jest tutaj ideałem. W tej chwili prawie powiedziałbym, że język musi mieć książkę wydaną przez O'Reilly. To staje się testem znaczenia dla hakerów.

Powinna być również dokumentacja online. W rzeczywistości książka może zacząć się od dokumentacji online. Ale nie sądzę, że książki fizyczne są jeszcze przestarzałe. Ich format jest wygodny, a cenzura de facto narzucona przez wydawców jest użytecznym, choć niedoskonałym filtrem. Księgarnie są jednym z najważniejszych miejsc do poznawania nowych języków.

3 Zwięzłość

Biorąc pod uwagę, że możesz dostarczyć trzy rzeczy, których potrzebuje każdy język — darmową implementację, książkę i coś do hakowania — jak stworzyć język, który hakerzy polubią?

Jedną z rzeczy, które lubią hakerzy, jest zwięzłość. Hakerzy są leniwi, w taki sam sposób, w jaki leniwi są matematycy i architekci modernistyczni: nienawidzą wszystkiego, co zbędne. Nie byłoby dalekie od prawdy stwierdzenie, że haker, który ma napisać program, decyduje, jakiego języka użyć, przynajmniej podświadomie, na podstawie całkowitej liczby znaków, które będzie musiał wpisać. Jeśli tak właśnie myślą hakerzy, projektant języka dobrze zrobi, działając tak, jakby tak było.

Błędem jest próba traktowania użytkownika jak dziecka za pomocą rozwlekłych wyrażeń, które mają przypominać angielski. Cobol jest notoryczny z tej wady. Haker uznałby, że musi napisać

dodaj x do y dając z

zamiast

z = x+y

za coś pomiędzy obrazą jego inteligencji a grzechem przeciwko Bogu.

Czasami mówiło się, że Lisp powinien używać first i rest zamiast car i cdr, ponieważ uczyniłoby to programy łatwiejszymi do czytania. Może przez pierwsze kilka godzin. Ale haker może szybko nauczyć się, że car oznacza pierwszy element listy, a cdr oznacza resztę. Użycie first i rest oznacza 50% więcej pisania. A także mają różne długości, co oznacza, że argumenty nie będą się wyrównywać, gdy są wywoływane, tak jak car i cdr często są, w kolejnych wierszach. Odkryłem, że to, jak kod układa się na stronie, ma duże znaczenie. Ledwo potrafię czytać kod Lisp, gdy jest ustawiony w czcionce o zmiennej szerokości, a przyjaciele mówią, że tak jest również w przypadku innych języków.

Zwięzłość to jedno miejsce, w którym języki silnie typowane przegrywają. Przy pozostałych równych warunkach, nikt nie chce zaczynać programu od grupy deklaracji. Wszystko, co może być domyślne, powinno być.

Poszczególne tokeny również powinny być krótkie. Perl i Common Lisp zajmują przeciwne bieguny w tej kwestii. Programy w Perlu mogą być prawie kryptograficznie gęste, podczas gdy nazwy wbudowanych operatorów Common Lisp są komicznie długie. Projektanci Common Lisp prawdopodobnie spodziewali się, że użytkownicy będą mieli edytory tekstu, które będą za nich wpisywać te długie nazwy. Ale koszt długiej nazwy to nie tylko koszt jej wpisania. Jest też koszt jej czytania i koszt miejsca, które zajmuje na ekranie.

4 Hackowalność

Jest jedna rzecz ważniejsza dla hakera niż zwięzłość: możliwość robienia tego, czego chce. W historii języków programowania zaskakująca ilość wysiłku poszła na zapobieganie programistom robienia rzeczy uważanych za niewłaściwe. Jest to niebezpiecznie arogancki plan. Skąd projektant języka ma wiedzieć, co programista będzie musiał zrobić? Myślę, że projektanci języków lepiej zrobią, traktując swojego docelowego użytkownika jako geniusza, który będzie musiał robić rzeczy, których nigdy nie przewidzieli, zamiast jako nieudacznika, którego trzeba chronić przed samym sobą. Nieudacznik i tak sobie strzeli w stopę. Możesz go uchronić przed odwoływaniem się do zmiennych w innym pakiecie, ale nie możesz go uchronić przed napisaniem źle zaprojektowanego programu, aby rozwiązać niewłaściwy problem, i poświęceniem na to wieczności.

Dobrzy programiści często chcą robić niebezpieczne i nieprzyzwoite rzeczy. Przez nieprzyzwoite mam na myśli rzeczy, które wykraczają poza jakąkolwiek fasadę semantyczną, którą język próbuje przedstawić: na przykład uzyskanie dostępu do wewnętrznej reprezentacji jakiejś abstrakcji wysokiego poziomu. Hakerzy lubią hakować, a hakowanie oznacza dostawanie się do wnętrza rzeczy i kwestionowanie pierwotnego projektanta.

Pozwól się kwestionować. Kiedy tworzysz jakiekolwiek narzędzie, ludzie używają go w sposób, którego nie przewidziałeś, a dotyczy to zwłaszcza bardzo złożonego narzędzia, jakim jest język programowania. Wielu hakerów będzie chciało dostosować twój model semantyczny w sposób, którego nigdy nie wyobrażałeś sobie. Mówię, pozwól im; daj programiście dostęp do jak największej liczby wewnętrznych elementów, o ile nie zagraża to systemom czasu wykonania, takim jak garbage collector.

W Common Lisp często chciałem iterować po polach struktury — na przykład, aby wyczyścić odniesienia do usuniętego obiektu lub znaleźć pola, które nie zostały zainicjalizowane. Wiem, że struktury są pod spodem tylko wektorami. A jednak nie mogę napisać ogólnej funkcji, którą mogę wywołać na dowolnej strukturze. Mogę tylko uzyskać dostęp do pól po nazwie, ponieważ tak ma być. Struktura ma znaczyć.

Haker może chcieć podważyć zamierzony model rzeczy tylko raz lub dwa razy w dużym programie. Ale jaką różnicę robi możliwość tego. I może to być więcej niż tylko kwestia rozwiązania problemu. Jest tu też pewna przyjemność. Hakerzy dzielą sekretną przyjemność chirurga z grzebania we wnętrznościach, sekretną przyjemność nastolatka z wyciskania pryszczy. [2] Dla chłopców, przynajmniej, pewne rodzaje horrorów są fascynujące. Maxim magazine publikuje coroczny album ze zdjęciami, zawierający mieszankę pin-upów i makabrycznych wypadków. Wiedzą, kto jest ich odbiorcą.

Historycznie rzecz biorąc, Lisp był dobry w pozwalaniu hakerom na realizację ich woli. Poprawność polityczna Common Lisp jest aberracją. Wczesne Lispy pozwalały na dostęp do wszystkiego. Znaczna część tego ducha jest, na szczęście, zachowana w makrach. Cóż za wspaniała rzecz, móc dokonywać dowolnych transformacji na kodzie źródłowym.

Klasyczne makra to prawdziwe narzędzie hakera — proste, potężne i niebezpieczne. Tak łatwo zrozumieć, co robią: wywołujesz funkcję na argumentach makra, a cokolwiek zwróci, jest wstawiane zamiast wywołania makra. Higieniczne makra ucieleśniają przeciwną zasadę. Starają się chronić cię przed zrozumieniem tego, co robią. Nigdy nie słyszałem, aby higieniczne makra były wyjaśnione w jednym zdaniu. I są klasycznym przykładem niebezpieczeństw związanych z decydowaniem, na co programiści mogą sobie pozwolić. Higieniczne makra mają mnie chronić przed przechwytywaniem zmiennych, między innymi, ale przechwytywanie zmiennych to dokładnie to, czego chcę w niektórych makrach.

Naprawdę dobry język powinien być zarówno czysty, jak i brudny: czysto zaprojektowany, z małym rdzeniem dobrze zrozumianych i wysoce ortogonalnych operatorów, ale brudny w tym sensie, że pozwala hakerom na realizację ich woli. C jest taki. Podobnie były wczesne Lispy. Prawdziwy język hakera zawsze będzie miał lekko podejrzany charakter.

Dobry język programowania powinien mieć cechy, które sprawiają, że ludzie używający frazy "inżynieria oprogramowania" kręcą głowami z dezaprobatą. Na drugim końcu kontinuum są języki takie jak Ada i Pascal, modele poprawności, które są dobre do nauczania i niewiele więcej.

5 Programy jednorazowe

Aby być atrakcyjnym dla hakerów, język musi być dobry do pisania programów, które chcą pisać. A to oznacza, co może być zaskakujące, że musi być dobry do pisania programów jednorazowych.

Program jednorazowy to program, który piszesz szybko do jakiegoś ograniczonego zadania: program do automatyzacji jakiegoś zadania administracji systemowej, generowania danych testowych do symulacji lub konwersji danych z jednego formatu na inny. Zaskakujące jest to, że programy jednorazowe, podobnie jak "tymczasowe" budynki wzniesione na wielu amerykańskich uniwersytetach podczas II wojny światowej, często nie są wyrzucane. Wiele z nich ewoluuje w prawdziwe programy, z prawdziwymi funkcjami i prawdziwymi użytkownikami.

Mam przeczucie, że najlepsze duże programy zaczynają życie w ten sposób, zamiast być projektowane jako duże od podstaw, jak tama Hoovera. Budowanie czegoś dużego od zera jest przerażające. Kiedy ludzie podejmują się zbyt dużego projektu, zostają przytłoczeni. Projekt albo utyka, albo rezultat jest sterylny i drewniany: centrum handlowe zamiast prawdziwego centrum miasta, Brasilia zamiast Rzymu, Ada zamiast C.

Innym sposobem na uzyskanie dużego programu jest rozpoczęcie od programu jednorazowego i ciągłe jego ulepszanie. To podejście jest mniej zniechęcające, a projekt programu korzysta z ewolucji. Myślę, że gdyby się przyjrzeć, okazałoby się, że tak właśnie powstaje większość dużych programów. A te, które ewoluowały w ten sposób, prawdopodobnie nadal są pisane w języku, w którym zostały pierwotnie napisane, ponieważ rzadko kiedy program jest przenoszony, chyba że z powodów politycznych. A zatem, paradoksalnie, jeśli chcesz stworzyć język używany do dużych systemów, musisz uczynić go dobrym do pisania programów jednorazowych, ponieważ stamtąd pochodzą duże systemy.

Perl jest uderzającym przykładem tej idei. Został nie tylko zaprojektowany do pisania programów jednorazowych, ale sam w sobie był w zasadzie programem jednorazowym. Perl zaczynał jako zbiór narzędzi do generowania raportów, a dopiero ewoluował w język programowania w miarę jak programy jednorazowe pisane w nim stawały się coraz większe. Dopiero Perl 5 (jeśli w ogóle) był odpowiedni do pisania poważnych programów, a jednak był już masowo popularny.

Co sprawia, że język jest dobry do programów jednorazowych? Przede wszystkim musi być łatwo dostępny. Program jednorazowy to coś, co spodziewasz się napisać w godzinę. Więc język prawdopodobnie musi być już zainstalowany na komputerze, którego używasz. Nie może to być coś, co musisz zainstalować przed użyciem. Musi tam być. C był tam, ponieważ pochodził z systemem operacyjnym. Perl był tam, ponieważ pierwotnie był narzędziem dla administratorów systemów, a twój już go zainstalował.

Bycie dostępnym oznacza jednak więcej niż tylko bycie zainstalowanym. Język interaktywny, z interfejsem wiersza poleceń, jest bardziej dostępny niż taki, który trzeba kompilować i uruchamiać osobno. Popularny język programowania powinien być interaktywny i szybko się uruchamiać.

Inną rzeczą, której potrzebujesz w programie jednorazowym, jest zwięzłość. Zwięzłość jest zawsze atrakcyjna dla hakerów, a nigdy bardziej niż w programie, który spodziewają się napisać w godzinę.

6 Biblioteki

Oczywiście ostateczna zwięzłość to posiadanie już napisanego programu, a jedynie jego wywołanie. I to prowadzi nas do tego, co uważam za coraz ważniejszą cechę języków programowania: funkcji bibliotecznych. Perl wygrywa, ponieważ ma duże biblioteki do manipulacji ciągami znaków. Ta klasa funkcji bibliotecznych jest szczególnie ważna dla programów jednorazowych, które często są pierwotnie pisane do konwersji lub ekstrakcji danych. Wiele programów w Perlu prawdopodobnie zaczyna się od kilku połączonych wywołań bibliotecznych.

Myślę, że wiele postępów, które nastąpią w językach programowania w ciągu najbliższych pięćdziesięciu lat, będzie dotyczyć funkcji bibliotecznych. Myślę, że przyszłe języki programowania będą miały biblioteki równie starannie zaprojektowane jak sam rdzeń języka. Projektowanie języków programowania nie będzie dotyczyć tego, czy uczynić język silnie czy słabo typowanym, obiektowym, funkcyjnym, czy czymkolwiek innym, ale tego, jak projektować wspaniałe biblioteki. Projektanci języków, którzy lubią myśleć o projektowaniu systemów typów, mogą się tego przerazić. To prawie jak pisanie aplikacji! Szkoda. Języki są dla programistów, a biblioteki są tym, czego programiści potrzebują.

Trudno jest projektować dobre biblioteki. To nie tylko kwestia pisania dużej ilości kodu. Gdy biblioteki stają się zbyt duże, czasami znalezienie potrzebnej funkcji zajmuje więcej czasu niż napisanie kodu samodzielnie. Biblioteki muszą być projektowane przy użyciu małego zestawu ortogonalnych operatorów, tak jak sam rdzeń języka. Powinno być możliwe, aby programista odgadł, jakie wywołanie biblioteczne zrobi to, czego potrzebuje.

Biblioteki to jedno miejsce, w którym Common Lisp sobie nie radzi. Istnieją tylko rudymentarne biblioteki do manipulacji ciągami znaków i prawie żadne do komunikacji z systemem operacyjnym. Z powodów historycznych, Common Lisp próbuje udawać, że system operacyjny nie istnieje. A ponieważ nie można komunikować się z systemem operacyjnym, mało prawdopodobne jest napisanie poważnego programu przy użyciu tylko wbudowanych operatorów w Common Lisp. Trzeba również użyć pewnych specyficznych dla implementacji hacków, a w praktyce te zazwyczaj nie dają wszystkiego, czego chcesz. Hakerzy myśleliby o Lisp znacznie wyżej, gdyby Common Lisp miał potężne biblioteki do obsługi ciągów znaków i dobre wsparcie systemu operacyjnego.

7 Składnia

Czy język z składnią Lisp, a dokładniej, brakiem składni, mógłby kiedyś stać się popularny? Nie znam odpowiedzi na to pytanie. Uważam, że składnia nie jest głównym powodem, dla którego Lisp nie jest obecnie popularny. Common Lisp ma gorsze problemy niż nieznana składnia. Znam kilku programistów, którzy są przyzwyczajeni do składni prefiksowej, a mimo to domyślnie używają Perla, ponieważ ma potężne biblioteki do obsługi ciągów znaków i może komunikować się z systemem operacyjnym.

Istnieją dwa potencjalne problemy ze składnią prefiksową: że jest ona nieznana programistom i że nie jest wystarczająco zwięzła. Konwencjonalna mądrość w świecie Lisp mówi, że pierwszy problem jest prawdziwy. Nie jestem tego taki pewien. Tak, składnia prefiksowa sprawia, że zwykli programiści panikują. Ale nie sądzę, żeby opinie zwykłych programistów miały znaczenie. Języki stają się popularne lub niepopularne na podstawie tego, co myślą o nich eksperci hakerzy, a myślę, że eksperci hakerzy mogliby sobie poradzić ze składnią prefiksową. Składnia Perla może być dość niezrozumiała, ale to nie przeszkodziło w popularności Perla. Jeśli już, to mogło to nawet pomóc w stworzeniu kultu Perla.

Bardziej poważnym problemem jest rozproszenie składni prefiksowej. Dla ekspertów hakerów to naprawdę problem. Nikt nie chce pisać (aref a x y), gdy mógłby napisać a[x,y].

W tym konkretnym przypadku istnieje sposób, aby obejść problem. Jeśli potraktujemy struktury danych jak funkcje na indeksach, moglibyśmy napisać (a x y) zamiast tego, co jest nawet krótsze niż forma Perla. Podobne sztuczki mogą skrócić inne rodzaje wyrażeń.

Możemy pozbyć się (lub uczynić opcjonalnymi) wielu nawiasów, czyniąc wcięcia znaczącymi. Tak właśnie programiści czytają kod: gdy wcięcia mówią jedno, a separatory drugie, kierujemy się wcięciami. Traktowanie wcięć jako znaczących wyeliminowałoby to częste źródło błędów, a także skróciłoby programy.

Czasami składnia infix jest łatwiejsza do czytania. Dotyczy to zwłaszcza wyrażeń matematycznych. Używałem Lisp przez całe życie programistyczne i nadal nie uważam prefiksowych wyrażeń matematycznych za naturalne. A jednak jest to wygodne, zwłaszcza gdy generujesz kod, mieć operatory, które przyjmują dowolną liczbę argumentów. Więc jeśli mamy składnię infix, powinna ona być prawdopodobnie zaimplementowana jako jakiś rodzaj makra odczytu.

Nie sądzę, abyśmy powinni być religijnie przeciwni wprowadzaniu składni do Lisp, o ile przekłada się ona na dobrze zrozumiane s-wyrażenia. W Lisp jest już sporo składni. Niekoniecznie jest źle wprowadzać więcej, o ile nikt nie jest zmuszony do jej używania. W Common Lisp niektóre separatory są zarezerwowane dla języka, co sugeruje, że przynajmniej niektórzy projektanci zamierzali mieć więcej składni w przyszłości.

Jednym z najbardziej rażąco nie-lispowych elementów składni w Common Lisp są ciągi formatujące; format jest sam w sobie językiem, a ten język nie jest Lisp. Gdyby istniał plan wprowadzania większej ilości składni do Lisp, specyfikatory formatu mogłyby być w niego włączone. Dobrze by było, gdyby makra mogły generować specyfikatory formatu w taki sam sposób, w jaki generują każdy inny rodzaj kodu.

Wybitny haker Lisp powiedział mi, że jego kopia CLTL otwiera się na sekcji format. Moja też. Prawdopodobnie świadczy to o możliwości poprawy. Może to również oznaczać, że programy wykonują dużo operacji I/O.

8 Wydajność

Dobry język, jak wszyscy wiedzą, powinien generować szybki kod. Ale w praktyce nie sądzę, że szybki kod pochodzi głównie z rzeczy, które robisz w projekcie języka. Jak dawno temu zauważył Knuth, szybkość ma znaczenie tylko w pewnych krytycznych wąskich gardłach. I jak wielu programistów zaobserwowało od tego czasu, bardzo często myli się co do tego, gdzie te wąskie gardła się znajdują.

Więc w praktyce, sposób na uzyskanie szybkiego kodu to posiadanie bardzo dobrego profilera, a nie na przykład uczynienie języka silnie typowanym. Nie musisz znać typu każdego argumentu w każdym wywołaniu w programie. Musisz być w stanie zadeklarować typy argumentów w wąskich gardłach. A jeszcze bardziej, musisz być w stanie dowiedzieć się, gdzie te wąskie gardła się znajdują.

Jedną z uwag, jakie ludzie mieli do Lisp, jest to, że trudno powiedzieć, co jest kosztowne. Może to być prawda. Może to być również nieuniknione, jeśli chcesz mieć bardzo abstrakcyjny język. A w każdym razie myślę, że dobry profiling znacznie przyczyniłby się do rozwiązania problemu: szybko nauczyłbyś się, co jest kosztowne.

Częścią problemu jest tutaj aspekt społeczny. Projektanci języków lubią pisać szybkie kompilatory. Tak mierzą swoje umiejętności. Uważają profiler za dodatek, w najlepszym razie. Ale w praktyce dobry profiler może zrobić więcej dla poprawy szybkości rzeczywistych programów napisanych w języku niż kompilator generujący szybki kod. Tutaj znowu projektanci języków są nieco oderwani od swoich użytkowników. Wykonują naprawdę dobrą robotę, rozwiązując nieco niewłaściwy problem.

Dobrym pomysłem może być posiadanie aktywnego profilera — przekazywanie danych o wydajności programiście zamiast czekania, aż sam o nie poprosi. Na przykład edytor mógłby wyświetlać wąskie gardła na czerwono, gdy programista edytuje kod źródłowy. Innym podejściem byłoby jakoś reprezentowanie tego, co dzieje się w działających programach. Byłoby to szczególnie korzystne w aplikacjach serwerowych, gdzie masz wiele działających programów do przejrzenia. Aktywny profiler mógłby graficznie pokazać, co dzieje się w pamięci podczas działania programu, a nawet wydawać dźwięki informujące o tym, co się dzieje.

Dźwięk jest dobrym wskaźnikiem problemów. W miejscu, w którym pracowałem, mieliśmy dużą tablicę wskaźników pokazujących, co dzieje się z naszymi serwerami internetowymi. Wskaźniki były poruszane przez małe serwomotory, które wydawały cichy dźwięk podczas obracania się. Nie widziałem tablicy z mojego biurka, ale zauważyłem, że mogłem natychmiast rozpoznać problem z serwerem po dźwięku.

Możliwe byłoby nawet napisanie profilera, który automatycznie wykrywałby nieefektywne algorytmy. Nie zdziwiłbym się, gdyby pewne wzorce dostępu do pamięci okazały się pewnymi oznakami złych algorytmów. Gdyby w komputerze biegał mały człowieczek wykonujący nasze programy, prawdopodobnie miałby tak długą i żałosną opowieść o swojej pracy, jak pracownik rządu federalnego. Często mam wrażenie, że wysyłam procesor na wiele bezcelowych poszukiwań, ale nigdy nie miałem dobrego sposobu, aby zobaczyć, co robi.

Wiele Lispsów kompiluje się teraz do kodu bajtowego, który jest następnie wykonywany przez interpreter. Zazwyczaj robi się to, aby ułatwić przenoszenie implementacji, ale może to być użyteczna funkcja języka. Dobrym pomysłem może być uczynienie kodu bajtowego oficjalną częścią języka i umożliwienie programistom używania kodu bajtowego w wąskich gardłach. Wtedy takie optymalizacje byłyby również przenośne.

Natura szybkości, postrzegana przez użytkownika końcowego, może się zmieniać. Wraz z rozwojem aplikacji serwerowych, coraz więcej programów może okazać się ograniczonych przez I/O. Warto byłoby przyspieszyć I/O. Język może pomóc w prostych środkach, takich jak proste, szybkie, formatowane funkcje wyjściowe, a także w głębokich zmianach strukturalnych, takich jak buforowanie i trwałe obiekty.

Użytkownicy są zainteresowani czasem reakcji. Ale inny rodzaj wydajności będzie coraz ważniejszy: liczba jednoczesnych użytkowników, których można obsłużyć na procesor. Wiele interesujących aplikacji pisanych w najbliższej przyszłości będzie aplikacjami serwerowymi, a liczba użytkowników na serwer jest kluczową kwestią dla każdego, kto hostuje takie aplikacje. W kosztach kapitałowych firmy oferującej aplikację serwerową jest to dzielnik.

Przez lata wydajność nie miała większego znaczenia w większości aplikacji dla użytkowników końcowych. Deweloperzy mogli zakładać, że każdy użytkownik będzie miał coraz potężniejszy procesor na swoim biurku. A zgodnie z Prawem Parkinsona, oprogramowanie rozszerzało się, aby wykorzystać dostępne zasoby. To się zmieni w przypadku aplikacji serwerowych. W tym świecie sprzęt i oprogramowanie będą dostarczane razem. Dla firm oferujących aplikacje serwerowe, kluczowe znaczenie dla wyniku będzie miało to, ilu użytkowników mogą obsłużyć na serwer.

W niektórych aplikacjach procesor będzie czynnikiem ograniczającym, a szybkość wykonania będzie najważniejsza do optymalizacji. Ale często ograniczeniem będzie pamięć; liczba jednoczesnych użytkowników będzie określana przez ilość pamięci potrzebnej na dane każdego użytkownika. Język może również pomóc tutaj. Dobre wsparcie dla wątków umożliwi wszystkim użytkownikom współdzielenie jednego stosu. Może również pomóc posiadanie trwałych obiektów i/lub wsparcia na poziomie języka dla leniwego ładowania.

9 Czas

Ostatnim składnikiem, którego potrzebuje popularny język, jest czas. Nikt nie chce pisać programów w języku, który może zniknąć, tak jak wiele języków programowania. Większość hakerów będzie więc czekać, aż język będzie dostępny przez kilka lat, zanim w ogóle rozważy jego użycie.

Wynalazcy wspaniałych nowych rzeczy często zaskakują się, odkrywając to, ale potrzebujesz czasu, aby przekazać ludziom jakąkolwiek wiadomość. Mój przyjaciel rzadko robi coś za pierwszym razem, gdy ktoś go o to prosi. Wie, że ludzie czasami proszą o rzeczy, których potem nie chcą. Aby uniknąć marnowania jego czasu, czeka, aż zostanie poproszony o coś po raz trzeci lub czwarty; do tego czasu osoba prosząca może być dość zirytowana, ale przynajmniej prawdopodobnie naprawdę chce tego, o co prosi.

Większość ludzi nauczyła się filtrować nowe rzeczy, o których słyszą. Nie zaczynają zwracać uwagi, dopóki nie usłyszą o czymś dziesięć razy. Są całkowicie usprawiedliwieni: większość nowych gorących rzeczy okazuje się stratą czasu i ostatecznie znika. Opóźniając naukę VRML, uniknąłem konieczności nauki tego w ogóle.

Więc każdy, kto wymyśla coś nowego, musi spodziewać się powtarzania swojej wiadomości przez lata, zanim ludzie zaczną ją rozumieć. Napisaliśmy coś, co było, o ile wiem, pierwszą aplikacją opartą na serwerze WWW, i zajęło nam lata, aby ludzie zrozumieli, że nie trzeba jej pobierać. Nie byli głupi. Po prostu nas ignorowali.

Dobra wiadomość jest taka, że proste powtarzanie rozwiązuje problem. Wszystko, co musisz zrobić, to opowiadać swoją historię, a w końcu ludzie zaczną słuchać. Nie wtedy, gdy ludzie zauważą, że tam jesteś, zwracają uwagę; ale wtedy, gdy zauważą, że nadal tam jesteś.

Dobrze, że zdobycie rozpędu zazwyczaj zajmuje trochę czasu. Większość technologii ewoluuje znacznie nawet po ich pierwszym uruchomieniu — zwłaszcza języki programowania. Nic nie może być lepsze dla nowej technologii niż kilka lat używania jej tylko przez niewielką liczbę wczesnych użytkowników. Wczesni użytkownicy są wyrafinowani i wymagający, i szybko eliminują wszelkie pozostałe wady w twojej technologii. Kiedy masz tylko kilku użytkowników, możesz być z nimi w bliskim kontakcie. A wczesni użytkownicy są wyrozumiali, gdy ulepszasz swój system, nawet jeśli powoduje to pewne przerwy.

Istnieją dwa sposoby wprowadzania nowych technologii: metoda wzrostu organicznego i metoda wielkiego wybuchu. Metoda wzrostu organicznego jest przykładem klasycznego, niedofinansowanego startupu garażowego. Kilku facetów, pracujących w ukryciu, opracowuje nową technologię. Wprowadzają ją bez marketingu i początkowo mają tylko kilku (fanatycznie oddanych) użytkowników. Kontynuują ulepszanie technologii, a w międzyczasie ich baza użytkowników rośnie dzięki poczcie pantoflowej. Zanim się zorientują, stają się duzi.

Drugie podejście, metoda wielkiego wybuchu, jest przykładem startupu wspieranego przez VC, mocno promowanego. Spieszą się z opracowaniem produktu, wprowadzają go z wielką publicity i natychmiast (mają nadzieję) mają dużą bazę użytkowników.

Generalnie, faceci z garażu zazdroszczą facetom z wielkiego wybuchu. Facety z wielkiego wybuchu są gładcy i pewni siebie oraz szanowani przez VC. Mogą sobie pozwolić na wszystko, co najlepsze, a kampania PR otaczająca premierę ma efekt uboczny w postaci uczynienia ich celebrytami. Facety z organicznym wzrostem, siedzący w swoim garażu, czują się biedni i nienawidzeni. A jednak myślę, że często mylą się, litując się nad sobą. Wzrost organiczny wydaje się przynosić lepszą technologię i bogatszych założycieli niż metoda wielkiego wybuchu. Jeśli spojrzysz na dominujące technologie dzisiaj, odkryjesz, że większość z nich rosła organicznie.

Ten wzorzec dotyczy nie tylko firm. Widzisz to również w badaniach sponsorowanych. Multics i Common Lisp były projektami wielkiego wybuchu, a Unix i MacLisp były projektami wzrostu organicznego.

10 Przeprojektowanie

"Najlepsze pisanie to przepisywanie", napisał E. B. White. Każdy dobry pisarz to wie, i to samo dotyczy oprogramowania. Najważniejszą częścią projektowania jest przeprojektowanie. Języki programowania, zwłaszcza, nie są przeprojektowywane wystarczająco.

Aby napisać dobre oprogramowanie, musisz jednocześnie trzymać w głowie dwie przeciwstawne idee. Potrzebujesz naiwnej wiary młodego hakera w jego umiejętności, a jednocześnie sceptycyzmu weterana. Musisz być w stanie myśleć jak trudne to może być? jedną połową mózgu, myśląc jednocześnie nigdy to nie zadziała drugą.

Sztuczka polega na uświadomieniu sobie, że nie ma tu prawdziwej sprzeczności. Chcesz być optymistyczny i sceptyczny wobec dwóch różnych rzeczy. Musisz być optymistyczny co do możliwości rozwiązania problemu, ale sceptyczny co do wartości jakiegokolwiek rozwiązania, które masz do tej pory.

Ludzie, którzy wykonują dobrą pracę, często myślą, że cokolwiek, nad czym pracują, jest bezwartościowe. Inni widzą, co zrobili, i są pełni podziwu, ale twórca jest pełen obaw. Ten wzorzec nie jest przypadkiem: to obawa sprawiła, że praca była dobra.

Jeśli uda ci się utrzymać nadzieję i obawę w równowadze, będą one napędzać projekt do przodu tak samo, jak twoje dwie nogi napędzają rower. W pierwszej fazie dwu-cyklicznego silnika innowacji gorączkowo pracujesz nad jakimś problemem, zainspirowany pewnością, że uda ci się go rozwiązać. W drugiej fazie patrzysz na to, co zrobiłeś w zimnym świetle poranka i widzisz wszystkie jego wady bardzo wyraźnie. Ale dopóki twój krytyczny duch nie przeważa nad twoją nadzieją, będziesz w stanie spojrzeć na swój niewątpliwie niekompletny system i pomyśleć, jak trudno jest przejść resztę drogi?, kontynuując w ten sposób cykl.

Utrzymanie równowagi między tymi dwiema siłami jest trudne. U młodych hakerów dominuje optymizm. Tworzą coś, są przekonani, że jest świetne, i nigdy tego nie ulepszają. U starych hakerów dominuje sceptycyzm i nawet nie odważą się podejmować ambitnych projektów.

Wszystko, co możesz zrobić, aby utrzymać cykl przeprojektowania, jest dobre. Prozę można przepisywać raz po raz, dopóki nie będziesz z niej zadowolony. Ale oprogramowanie, co do zasady, nie jest przeprojektowywane wystarczająco. Proza ma czytelników, ale oprogramowanie ma użytkowników. Jeśli pisarz przepisuje esej, ludzie, którzy czytali poprzednią wersję, raczej nie będą narzekać, że ich myśli zostały zepsute przez jakąś nowo wprowadzoną niezgodność.

Użytkownicy to miecz obosieczny. Mogą pomóc ci ulepszyć swój język, ale mogą też zniechęcić cię do jego ulepszania. Więc wybieraj swoich użytkowników ostrożnie i powoli zwiększaj ich liczbę. Posiadanie użytkowników jest jak optymalizacja: mądrym posunięciem jest jej opóźnienie. Ponadto, jako ogólna zasada, możesz w dowolnym momencie zmienić więcej, niż myślisz. Wprowadzanie zmian jest jak zdejmowanie plastra: ból jest wspomnieniem prawie natychmiast, gdy go poczujesz.

Wszyscy wiedzą, że nie jest dobrym pomysłem, aby język był projektowany przez komitet. Komitety prowadzą do złego projektu. Ale myślę, że największym niebezpieczeństwem komitetów jest to, że zakłócają przeprojektowanie. Wprowadzenie zmian jest tak pracochłonne, że nikt nie chce się tym zajmować. Cokolwiek zdecyduje komitet, zazwyczaj tak pozostaje, nawet jeśli większość członków tego nie lubi.

Nawet komitet dwuosobowy przeszkadza w przeprojektowaniu. Dzieje się tak szczególnie w interfejsach między fragmentami oprogramowania napisanymi przez dwie różne osoby. Aby zmienić interfejs, obie muszą zgodzić się na jego zmianę jednocześnie. I tak interfejsy zazwyczaj w ogóle się nie zmieniają, co jest problemem, ponieważ zazwyczaj są one jedną z najbardziej ad hoc części każdego systemu.

Jednym z rozwiązań może być projektowanie systemów w taki sposób, aby interfejsy były poziome, a nie pionowe — aby moduły były zawsze pionowo ułożonymi warstwami abstrakcji. Wtedy interfejs będzie zazwyczaj należał do jednego z nich. Niższy z dwóch poziomów będzie albo językiem, w którym napisany jest wyższy, w którym to przypadku niższy poziom będzie posiadał interfejs, albo będzie niewolnikiem, w którym to przypadku interfejs może być dyktowany przez wyższy poziom.

11 Lisp

Wszystko to oznacza, że jest nadzieja dla nowego Lisp. Jest nadzieja dla każdego języka, który daje hakerom to, czego chcą, w tym Lisp. Myślę, że mogliśmy popełnić błąd, myśląc, że hakerzy są zniechęceni dziwnością Lisp. Ta pocieszająca iluzja mogła powstrzymać nas od dostrzeżenia prawdziwego problemu z Lisp, lub przynajmniej z Common Lisp, którym jest to, że ssie w robieniu tego, czego chcą hakerzy. Język hakera potrzebuje potężnych bibliotek i czegoś do hakowania. Common Lisp nie ma ani jednego, ani drugiego. Język hakera jest zwięzły i hackowalny. Common Lisp nie jest.

Dobra wiadomość jest taka, że to nie Lisp ssie, ale Common Lisp. Jeśli uda nam się opracować nowy Lisp, który będzie językiem hakera, myślę, że hakerzy go użyją. Będą używać każdego języka, który wykona zadanie. Wszystko, co musimy zrobić, to upewnić się, że ten nowy Lisp wykona jakieś ważne zadanie lepiej niż inne języki.

Historia oferuje pewną zachętę. Z czasem kolejne nowe języki programowania przejmowały coraz więcej cech Lisp. Nie pozostało już wiele do skopiowania, zanim język, który stworzyłeś, nie będzie Lisp. Najnowszy gorący język, Python, to rozcieńczony Lisp ze składnią infix i bez makr. Nowy Lisp byłby naturalnym krokiem w tej progresji.

Czasami myślę, że byłby to dobry chwyt marketingowy, aby nazwać go ulepszoną wersją Pythona. Brzmi to bardziej modnie niż Lisp. Dla wielu ludzi Lisp to powolny język AI z dużą ilością nawiasów. Oficjalna biografia Fritza Kunze ostrożnie unika wspomnienia o słowie L. Ale moje przypuszczenie jest takie, że nie powinniśmy bać się nazwać nowego Lisp Lisp. Lisp nadal cieszy się dużym, ukrytym szacunkiem wśród najlepszych hakerów — tych, którzy na przykład przeszli 6.001 i zrozumieli ją. A to są użytkownicy, których trzeba zdobyć.

W "Jak zostać hakerem" Eric Raymond opisuje Lisp jako coś w rodzaju łaciny lub greki — język, którego powinieneś się nauczyć jako ćwiczenie intelektualne, nawet jeśli faktycznie go nie użyjesz:

Warto nauczyć się Lisp dla głębokiego doświadczenia oświecenia, które będziesz miał, gdy w końcu go zrozumiesz; to doświadczenie sprawi, że będziesz lepszym programistą przez resztę swoich dni, nawet jeśli nigdy faktycznie nie będziesz dużo używać samego Lisp.

Gdybym nie znał Lisp, czytanie tego sprawiłoby, że zacząłbym zadawać pytania. Język, który uczyniłby mnie lepszym programistą, jeśli cokolwiek znaczy, oznacza język, który byłby lepszy do programowania. I to jest w rzeczywistości implikacja tego, co mówi Eric.

Dopóki ta idea krąży, myślę, że hakerzy będą wystarczająco otwarci na nowy Lisp, nawet jeśli będzie nazywał się Lisp. Ale ten Lisp musi być językiem hakera, jak klasyczne Lispy z lat 70. Musi być zwięzły, prosty i hackowalny. I musi mieć potężne biblioteki do robienia tego, czego hakerzy chcą teraz.

W kwestii bibliotek myślę, że jest miejsce, aby pokonać języki takie jak Perl i Python w ich własnej grze. Wiele nowych aplikacji, które będą potrzebne w nadchodzących latach, to aplikacje serwerowe. Nie ma powodu, aby nowy Lisp nie miał bibliotek do obsługi ciągów znaków równie dobrych jak Perl, a jeśli ten nowy Lisp miałby również potężne biblioteki do aplikacji serwerowych, mógłby być bardzo popularny. Prawdziwi hakerzy nie wzgardzą nowym narzędziem, które pozwoli im rozwiązać trudne problemy za pomocą kilku wywołań bibliotecznych. Pamiętaj, hakerzy są leniwi.

Mogłoby to być jeszcze większym zwycięstwem, aby mieć podstawowe wsparcie językowe dla aplikacji serwerowych. Na przykład, jawne wsparcie dla programów z wieloma użytkownikami, lub własność danych na poziomie tagów typów.

Aplikacje serwerowe dają nam również odpowiedź na pytanie, do czego będzie służył ten nowy Lisp. Nie zaszkodziłoby uczynić Lisp lepszym jako językiem skryptowym dla Uniksa. (Trudno byłoby go pogorszyć.) Ale myślę, że są obszary, w których łatwiej byłoby pokonać istniejące języki. Myślę, że lepiej byłoby podążać za modelem Tcl i dostarczyć Lisp wraz z kompletnym systemem do obsługi aplikacji serwerowych. Lisp jest naturalnym dopasowaniem do aplikacji serwerowych. Zamknięcia leksykalne zapewniają sposób na uzyskanie efektu podprogramów, gdy interfejs użytkownika to tylko seria stron internetowych. S-wyrażenia ładnie mapują się na HTML, a makra są dobre do jego generowania. Potrzebne są lepsze narzędzia do pisania aplikacji serwerowych, a potrzebny jest nowy Lisp, a te dwa dobrze by ze sobą współpracowały.

12 Język marzeń

Podsumowując, spróbujmy opisać język marzeń hakera. Język marzeń jest piękny, czysty i zwięzły. Ma interaktywny toplevel, który szybko się uruchamia. Możesz pisać programy do rozwiązywania powszechnych problemów przy użyciu bardzo małej ilości kodu. Prawie cały kod w każdym programie, który piszesz, to kod specyficzny dla twojej aplikacji. Wszystko inne zostało zrobione za ciebie.

Składnia języka jest aż do przesady zwięzła. Nigdy nie musisz wpisywać niepotrzebnego znaku, ani nawet często używać klawisza Shift.

Używając dużych abstrakcji, możesz bardzo szybko napisać pierwszą wersję programu. Później, gdy chcesz zoptymalizować, jest bardzo dobry profiler, który mówi ci, gdzie skupić swoją uwagę. Możesz sprawić, że wewnętrzne pętle będą oślepiająco szybkie, nawet pisząc kod bajtowy w linii, jeśli tego potrzebujesz.

Istnieje wiele dobrych przykładów do nauki, a język jest na tyle intuicyjny, że możesz nauczyć się go używać z przykładów w kilka minut. Nie musisz często zaglądać do instrukcji. Instrukcja jest cienka i zawiera niewiele ostrzeżeń i kwalifikacji.

Język ma mały rdzeń i potężne, wysoce ortogonalne biblioteki, które są równie starannie zaprojektowane jak sam rdzeń języka. Biblioteki wszystkie dobrze ze sobą współpracują; wszystko w języku pasuje do siebie jak części w doskonałym aparacie fotograficznym. Nic nie jest przestarzałe ani zachowane ze względu na kompatybilność. Kod źródłowy wszystkich bibliotek jest łatwo dostępny. Łatwo jest komunikować się z systemem operacyjnym i z aplikacjami napisanymi w innych językach.

Język jest zbudowany warstwowo. Abstrakcje wyższego poziomu są zbudowane w bardzo przejrzysty sposób z abstrakcji niższego poziomu, do których możesz uzyskać dostęp, jeśli chcesz.

Nic nie jest przed tobą ukryte, co nie musi być absolutnie ukryte. Język oferuje abstrakcje tylko jako sposób na oszczędzenie pracy, a nie jako sposób na powiedzenie ci, co robić. W rzeczywistości język zachęca cię do bycia równoprawnym uczestnikiem jego projektowania. Możesz zmienić wszystko w nim, w tym nawet jego składnię, a wszystko, co napiszesz, ma, w miarę możliwości, taki sam status jak to, co jest wstępnie zdefiniowane.

Przypisy

[1] Makra bardzo zbliżone do współczesnego pomysłu zostały zaproponowane przez Timothy'ego Harta w 1964 roku, dwa lata po wydaniu Lisp 1.5. Początkowo brakowało sposobów na uniknięcie przechwytywania zmiennych i wielokrotnej ewaluacji; przykłady Harta podlegają obu.

[2] W książce When the Air Hits Your Brain, neurochirurg Frank Vertosick opowiada o rozmowie, w której jego starszy rezydent, Gary, mówi o różnicy między chirurgami a internistami ("pchłami"):

Gary i ja zamówiliśmy dużą pizzę i znaleźliśmy wolny stolik. Szef zapalił papierosa. "Spójrz na te cholerne pchły, gadające o jakiejś chorobie, którą zobaczą raz w życiu. To jest problem z pchłami, lubią tylko dziwne rzeczy. Nienawidzą swoich codziennych przypadków. To jest różnica między nami a pieprzonymi pchłami. Widzisz, kochamy duże, soczyste przepukliny dysku lędźwiowego, ale oni nienawidzą nadciśnienia..."

Trudno jest myśleć o przepuklinie dysku lędźwiowego jako o soczystej (chyba że dosłownie). A jednak myślę, że wiem, co mają na myśli. Często miałem soczystego buga do wytropienia. Ktoś, kto nie jest programistą, miałby trudności z wyobrażeniem sobie, że może być przyjemność w bugu. Na pewno lepiej, gdyby wszystko działało bez zarzutu. Pod pewnym względem tak jest. A jednak niewątpliwie jest pewna ponura satysfakcja z tropienia pewnych rodzajów błędów.